Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is the size of make_shared two pointers?

As illustrated in the code here, the size of the object returned from make_shared is two pointers.

However, why doesn't make_shared work like the following (assume T is the type we're making a shared pointer to):

The result of make_shared is one pointer in size, which points to of allocated memory of size sizeof(int) + sizeof(T), where the int is a reference count, and this gets incremented and decremented on construction/destruction of the pointers.

unique_ptrs are only the size of one pointer, so I'm not sure why shared pointer needs two. As far as I can tell, all it needs a reference count, which with make_shared, can be placed with the object itself.

Also, is there any implementation that is implemented the way I suggest (without having to muck around with intrusive_ptrs for particular objects)? If not, what is the reason why the implementation I suggest is avoided?

like image 713
Clinton Avatar asked Jul 26 '11 06:07

Clinton


People also ask

What is the size of shared pointer?

The result of make_shared is one pointer in size, which points to of allocated memory of size sizeof(int) + sizeof(T) , where the int is a reference count, and this gets incremented and decremented on construction/destruction of the pointers.

Why is Make_shared more efficient?

The statement that uses make_shared is simpler because there's only one function call involved. It's more efficient because the library can make a single allocation for both the object and the smart pointer.

What is Make_shared pointer?

Make shared_ptr. Allocates and constructs an object of type T passing args to its constructor, and returns an object of type shared_ptr<T> that owns and stores a pointer to it (with a use count of 1). This function uses ::new to allocate storage for the object.

Why should I use Make_shared?

As well as this efficiency, using make_shared means that you don't need to deal with new and raw pointers at all, giving better exception safety - there is no possibility of throwing an exception after allocating the object but before assigning it to the smart pointer.


1 Answers

In all implementations I'm aware of, shared_ptr stores the owned pointer and the reference count in the same memory block. This is contrary to what other answers are saying. Additionally a copy of the pointer will be stored in the shared_ptr object. N1431 describes the typical memory layout.

It is true that one can build a reference counted pointer with sizeof only one pointer. But std::shared_ptr contains features that absolutely demand a sizeof two pointers. One of those features is this constructor:

template<class Y> shared_ptr(const shared_ptr<Y>& r, T *p) noexcept;      Effects: Constructs a shared_ptr instance that stores p              and shares ownership with r.      Postconditions: get() == p && use_count() == r.use_count() 

One pointer in the shared_ptr is going to point to the control block owned by r. This control block is going to contain the owned pointer, which does not have to be p, and typically isn't p. The other pointer in the shared_ptr, the one returned by get(), is going to be p.

This is referred to as aliasing support and was introduced in N2351. You may note that shared_ptr had a sizeof two pointers prior to the introduction of this feature. Prior to the introduction of this feature, one could possibly have implemented shared_ptr with a sizeof one pointer, but no one did because it was impractical. After N2351, it became impossible.

One of the reasons it was impractical prior to N2351 was because of support for:

shared_ptr<B> p(new A); 

Here, p.get() returns a B*, and has generally forgotten all about the type A. The only requirement is that A* be convertible to B*. B may derive from A using multiple inheritance. And this implies that the value of the pointer itself may change when converting from A to B and vice-versa. In this example, shared_ptr<B> needs to remember two things:

  1. How to return a B* when get() is called.
  2. How to delete a A* when it is time to do so.

A very nice implementation technique to accomplish this is to store the B* in the shared_ptr object, and the A* within the control block with the reference count.

like image 163
Howard Hinnant Avatar answered Sep 18 '22 19:09

Howard Hinnant