Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

new and make_shared for shared pointers

I came across this post and one of the answers by @kerek SB states

std::shared_ptr<Object> p1 = std::make_shared<Object>("foo");
std::shared_ptr<Object> p2(new Object("foo"));

In your code, the second variable is just a naked pointer, not a shared pointer at all.

Now on the meat. make_shared is (in practice) more efficient, because it allocates the reference control block together with the actual object in one single dynamic allocation. By contrast, the constructor for shared_ptr that takes a naked object pointer must allocate another dynamic variable for the reference count. The trade-off is that make_shared (or its cousin allocate_shared) does not allow you to specify a custom deleter, since the allocation is performed by the allocator.

(This does not affect the construction of the object itself. From Object's perspective there is no difference between the two versions. What's more efficient is the shared pointer itself, not the managed object.)

Now I have two questions regarding this post and would appreciate it if someone could clarify this

  1. Why is the second one not a shared pointer ? Will that not increment a reference count

  2. How does make_shared only make one memory allocation and new makes two thus making make_shared more efficent ?

A little clarification on this would be appreciated.

like image 609
MistyD Avatar asked Nov 22 '14 21:11

MistyD


Video Answer


1 Answers

In that question, the "second variable" referred to this line:

auto ptr_res2(new Object("new")); // this creates an Object*

Not this one:

std::shared_ptr<Object> p2(new Object("foo")); // this creates a shared_ptr<Object>

The best explanation for why make_shared is more efficient with one allocation is to compare images. Here is what std_shared_ptr<Object>(new Object) looks like:

enter image description here

The shared_ptr has a Widget*, but it's not in the same memory block as the ref counters since they were allocated separately. The Widget* was passed in, and the ref counting block was allocated internally, which is why the Widget is in a separate memory space.

On the other hand, here is what it looks like with one allocation:

enter image description here

(I'm stealing both pictures from Herb Sutter). We still need a Widget and a ref counting block, but instead the whole memory block is grabbed in a single call to new / malloc, just of sufficient size, so the Widget and ref counting block end up contiguous in memory.

like image 106
Barry Avatar answered Sep 17 '22 22:09

Barry