The other key terminology you need to understand ownership is
lifetime.
Every object has a lifetime. That is defined as the time between when it is initially created and when it is destroyed. For variables in a function, this is automatically managed by the compiler for you, and has been since the dawn of C++.
void function() {
MyObject A; // A begins its lifetime here
MyObject* ptr;
while (condition) {
MyObject B; // B begins its lifetime here
ptr = &B;
other_function();
} // B ends its lifetime here, as this is the end of the scope in which B was declared
A = *ptr; // BUG!! ptr points to an object whose lifetime is over, so ptr points to garbage!
} // A ends its lifetime here, as this is the end of the scope in which A was declare
That illustrates lifetimes for simple C++ code, and also illustrates a bug that happens when you don't pay attention to lifetimes.
Again, the above was all automatic. The compiler determined when lifetimes began and end based on declarations and scopes. There's nothing special you have to do other than to be sure not to break the rules (like the bug in the code sample).
Ownership is a term that means that the lifetime of one object is managed explicitly by user code instead of the compiler.
Local variables are always managed by the compiler, so clearly this must mean heap-allocated objects (e.g. things that directly or indirectly allocated with operator new or the like).
void function() {
MyObject* A; // A does not yet refer to any object.
MyObject* B;
A = new MyObject; // A now refers to a new object, so we can say the lifetime of *A begins now
B = A;
some_function(*A);
delete A; // we have explicitly deleted *A, so its lifetime is now over
delete B; // BUG!! B pointed to the same object A did, which we already deleted!
}
Here we see explicit lifetime management. Note that 'A' and 'B' themselves are local variables that have automatic lifetime, but the object *A pointed to by A has explicit lifetime.
In this case, we would say that *A is
owned by A. If A doesn't delete the object then you will probably leak, because nothing else in the code knows that it's supposed to delete *A.
However, there's danger here: you might forget to delete the object or you might lose track of who "owns" the object (leading to the bug were we tried to double-delete the same object).
Smart pointers automatically delete things, basically speaking. Smart pointers avoid both possible bugs above; they explicitly know they own another object and hence know when to delete the object safely... IF you don't do bad things with smart pointers.
The trick is just to understand these rules and problems. If you understand that objects all have lifetimes, and that only one piece of code "owns" each object and is responsible for deleting it, you can safely use smart pointers (and regular pointers): smart pointers just become a convenience to help you avoid forgetting or losing track.