New and make_shared for shared pointers

I came across this post and one of the answers from @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 bare pointer, not a shared pointer.

Now about the meat. make_shared (in practice) is more efficient because it allocates a control block of control along with the actual object in one dynamic distribution. In contrast, the constructor for shared_ptr, which accepts an open object pointer, must allocate another dynamic variable for the reference counter. The trade-off is that make_shared (or its cousin allocate_shared) does not allow you to specify a custom div, as distribution is done using the Distributor.

(This does not affect the design of the object itself. The object perspective does not differ between the two versions. A more general pointer, not a managed object, is more efficient.)

Now I have two questions regarding this post, and I would appreciate it if anyone could clarify this.

  • Why is the second not a common pointer? Will this not increase the number of links

  • How does make_shared only make one memory allocation, and new does two, in a way that makes make_shared more efficient?

A little clarification on this will be appreciated.

+6
source share
4 answers
  • The code called the second variable is actually this (taken from OP code):

     auto ptr_res2(new Object("new")); 

    This does not create std::shared_ptr , it creates a pointer to Object .

  • When creating std::shared_ptr using your constructor, which takes a bare pointer, you must pass a pointer to the memory already allocated (for example, allocated using new ). This means that the memory for the object was already allocated when the std::shared_ptr object was created. std::shared_ptr should allocate memory for its own actions, for example, for example. control counter. Thus, there are 2 distributions: one that uses new , passed to ctor std::shared_ptr , and one that is required when building std::shared_ptr .

     Object* ptr = new Object{"Foo"}; // Allocation 1 (object). std::shared_ptr<Object> p1{ptr}; // Allocation 2 (internal counters). 

    The helper function std::make_shared uses only 1 selection, because you pass it the arguments needed to create the object , not a pointer to the object itself. std::make_shared can then allocate memory once, which contains both the object and the ref counter.

     auto p2 = std::make_shared<Object>{"Foo"} // Allocation 1 (object & counter). 
+3
source

In this question, the "second variable" refers 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 of why make_shared is more efficient with a single distribution is image comparison. Here is what std_shared_ptr<Object>(new Object) looks like:

enter image description here

shared_ptr has Widget* , but it is not in the same memory block as the ref counts, since they were allocated separately. Widget* was passed, and the ref count was allocated internally, so Widget is in a separate memory space.

On the other hand, here's what it looks like with one highlight:

enter image description here

(I steal both paintings from Herb Sutter). We still need the Widget and ref counting block, but instead the whole memory block is captured by a single call to new / malloc only of sufficient size, therefore the Widget and ref counting block is continuous in memory.

+6
source

Why is the second not a common pointer? Will this not increase the number of links

I believe the quote refers to the source code of the poster, which claims to create a smart pointer, but doesn’t actually. ptr_res2 is 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 make_shared only does one memory allocation, and the new one does two, which makes make_shared more efficient

make_shared needs to allocate a slot for the counter and the object itself. You can allocate memory at a time, and then use part of it for the counter and the rest for the object.

Please note that this also means that the counter and the object itself are next to each other in memory, thereby improving data locality.

+2
source
  • The second is still a common pointer. It calls the constructor for shared_ptr:

http://www.cplusplus.com/reference/memory/shared_ptr/shared_ptr/

  1. However, using make_shared is more efficient because it only performs one distribution, not 2 distributions. Remember that shared_ptr requires heap space for Object, as well as a manager to track the number of shared and weak pointers. If you use a constructor, you allocate space for the Object, and then pass the pointer to the constructor, which should allocate space for the manager. Instead, if you use make_shared, it allocates one piece of memory in which Object and the manager are stored. Since distribution is relatively expensive, one distribution is better than two.
+1
source

Source: https://habr.com/ru/post/978553/


All Articles