I generally agree with James McNellis's answer. However, there is one more point that should be mentioned.
As you know, shared_ptr<T> can also be used when the type T not fully defined.
I.e:
class AbraCadabra; boost::shared_ptr<AbraCadabra> myPtr;
This will compile and work. Unlike many other smart pointer implementations that actually require encapsulated types to be fully defined for their use. This is because the smart pointer must know in order to delete an encapsulated object when it is no longer referenced, and to delete an object one must know what it is.
This is achieved using the following trick: shared_ptr actually consists of the following:
- Opaque object pointer
- General link counts (James McNellis description)
- A pointer to a dedicated factory that knows how to destroy your object.
The above factory is a helper object with a single virtual function that should properly remove your object.
This factory is actually created when you assign a value to your generic pointer.
That is, the following code
AbraCadabra* pObj = ; myPtr.reset(pObj);
This factory is located here. Note. The reset function is actually a template . It actually creates a factory for the specified type (the type of the object passed as a parameter). Here your type should be fully defined. That is, if it is not yet defined, you will get a compilation error.
Please note: if you actually create an object of a derived type (derived from AbraCadabra ) and assign it to shared_ptr - it will be deleted correctly, even if your destructor is not virtual. shared_ptr always deletes an object according to the type that it sees in the reset function.
So, shared_ptr is a pretty complicated version of a smart pointer. It gives amazing flexibility. . However, you should be aware that this flexibility is achieved at the price of extremely poor performance compared to other possible smart pointer implementations.
On the other hand, there are so-called "intrusive" smart pointers. They do not have such flexibility, however, on the contrary, they give better performance.
The benefits of shared_ptr compared to intrusive smart pointers:
- Very flexible use. Only the encapsulated type needs to be defined only when
shared_ptr assigned. This is very important for large projects, significantly reduces dependency. - There should not be a virtual destructor in the encapsulated type, but polymorphic types will be deleted correctly.
- Can be used with weak pointers.
Cons of shared_ptr compared to intrusive smart pointers:
- Very barbaric performance and heap memory waste. When assigned, allocates 2 more objects: reference counters, plus factory (memory waste, slow). However, this only happens on
reset . When one shared_ptr assigned to another, nothing else is allocated. - The above may throw an exception. (condition for lack of memory). In contrast, intuitive smart pointers can never be thrown (except for process exceptions related to invalid memory access, stack overflows, etc.).
- Removing your object is also slow: you need to free two more structures.
When working with intrusive smart pointers, you can freely mix smart pointers with the source. This is normal because the actual reference count is inside the object itself, which is single. Unlike shared_ptr you can not mix with raw pointers.
AbraCadabra * pObj = / * get it from somewhere * /; myPtr.reset (PObj); // ... pObj = myPtr.get (); boost :: shared_ptr myPtr2 (pObj); // oops
The above will fail.