Here's a quote from cppreference's implementation note section of std::shared_ptr
, which mentions that there are two different pointers(as shown in bold) : the one that can be returned by get()
, and the one holding the actual data within the control block.
In a typical implementation,
std::shared_ptr
holds only two pointers:
- the stored pointer (one returned by
get()
)- a pointer to control block
The control block is a dynamically-allocated object that holds:
- either a pointer to the managed object or the managed object itself
- the deleter (type-erased)
- the allocator (type-erased)
- the number of
shared_ptrs
that own the managed object- the number of
weak_ptrs
that refer to the managed objectThe pointer held by the
shared_ptr
directly is the one returned byget()
, while the pointer or object held by the control block is the one that will be deleted when the number of shared owners reaches zero. These pointers are not necessarily equal.
My question is, why are two different pointer(the two in bold) needed for the managed object (in addition to the pointer to the control block)? Doesn't the one returned by get()
suffice? And why aren't these pointers necessarily equal?
Smart pointers are class objects that behave like raw pointers but manage objects that are new and when or whether to delete them— smart pointers automatically delete the managed object at the appropriate time.
The shared_ptr type is a smart pointer in the C++ standard library that is designed for scenarios in which more than one owner might have to manage the lifetime of the object in memory.
std::unique_ptr s are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.
In short: Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.
The reason for this is that you can have a shared_ptr
which points to something else than what it owns, and that is by design. This is implemented using the constructor listed as nr. 8 on cppreference:
template< class Y > shared_ptr( const shared_ptr<Y>& r, T *ptr );
A shared_ptr
created with this constructor shares ownership with r
, but points to ptr
. Consider this (contrived, but illustrating) code:
std::shared_ptr<int> creator() { using Pair = std::pair<int, double>; std::shared_ptr<Pair> p(new Pair(42, 3.14)); std::shared_ptr<int> q(p, &(p->first)); return q; }
Once this function exits, only a pointer to the int
subobject of the pair is available to client code. But because of the shared ownership between q
and p
, the pointer q
keeps the entire Pair
object alive.
Once dealloacation is supposed to happen, the pointer to the entire Pair
object must be passed to the deleter. Hence the pointer to the Pair
object must be stored somewhere alongside the deleter—in other words, in the control block.
For a less contrived example (probably even one closer to the original motivation for the feature), consider the case of pointing to a base class. Something like this:
struct Base1 { // ::: }; struct Base2 { // ::: }; struct Derived : Base1, Base2 { // ::: }; std::shared_ptr<Base2> creator() { std::shared_ptr<Derived> p(new Derived()); std::shared_ptr<Base2> q(p, static_cast<Base2*>(p.get())); return q; }
Of course, the real implementation of std::shared_ptr
has all the implicit conversions in place so that the p
-and-q
dance in creator
is not necessary, but I've kept it there to resemble the first example.
Additional link to @Angew 's answer:
Peter Dimov, Beman Dawes and Greg Colvin proposed shared_ptr and weak_ptr for inclusion in the Standard Library via the first Library Technical Report (known as TR1). The proposal was accepted and eventually went on to become a part of the C++ standard in its 2011 iteration.
boost smart pointer history
In this proposal, the authors pointed out that the usage of "Shared Pointer Aliasing":
Advanced users often require the ability to create a shared_ptr instance p that shares ownership with another (master) shared_ptr q but points to an object that is not a base of *q. *p may be a member or an element of *q, for example. This section proposes an additional constructor that can be used for this purpose.
So they add an additional pointer into the control block.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With