Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why are two raw pointers to the managed object needed in std::shared_ptr implementation?

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:

  1. the stored pointer (one returned by get())
  2. a pointer to control block

The control block is a dynamically-allocated object that holds:

  1. either a pointer to the managed object or the managed object itself
  2. the deleter (type-erased)
  3. the allocator (type-erased)
  4. the number of shared_ptrs that own the managed object
  5. the number of weak_ptrs that refer to the managed object

The pointer held by the shared_ptr directly is the one returned by get(), 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?

like image 767
btshengsheng Avatar asked Dec 02 '15 15:12

btshengsheng


People also ask

Why do we need raw pointers in smart pointers?

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.

Why do we need shared pointer?

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.

What is smart pointer when should we use it asked me to implement unique_ptr of my own?

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.

Why would you choose shared_ptr instead of unique_ptr?

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.


2 Answers

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.

like image 181
Angew is no longer proud of SO Avatar answered Oct 14 '22 02:10

Angew is no longer proud of SO


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.

like image 30
qinggniq Avatar answered Oct 14 '22 03:10

qinggniq