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
Why is the second one not a shared pointer ? Will that not increment a reference count
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.
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:
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:
(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.
Why is the second one not a shared pointer ? Will that not increment a reference count
I believe the quote refers to the original poster's code, which claims to create a smart pointer but in fact does not do that. ptr_res2
is just a regular pointer.
cout << "Create smart_ptr using new..." << endl;
auto ptr_res2(new Object("new"));
cout << "Create smart_ptr using new: done." << endl;
How does make_shared only make one memory allocation and new makes two thus making make_shared more efficent
make_shared
needs to allocate a slot for the counter and the object itself. It's possible to allocate the memory in one go and then use part of it for the counter and the rest for the object.
Note that this will also mean that the counter and the object itself are right next to each other in memory thus improving data locality.
- The second one is still a shared pointer. It is calling the constructor for shared_ptr:
http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/
- However, using make_shared is more efficient because it is only doing one allocation rather than 2 allocations. Remember, a shared_ptr needs space on the heap for Object and also the manager to keep track of the number of shared and weak pointers. If you are using the constructor, you are allocating space for Object, then passing the pointer in to the constructor which needs to allocate space for the manager. Instead, if you use make_shared, it allocates one chunk of memory that stores Object and the manager. Because allocation is relatively expensive, one allocation is better than two.