Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What happens when using make_shared

I'm interested if these two lines of code are the same:

shared_ptr<int> sp(new int(1)); // double allocation? shared_ptr<int> sp(make_shared<int>(1)); // just one allocation? 

If this is true could someone please explain why is it only one allocation in the second line?

like image 399
Tracer Avatar asked Jul 16 '14 11:07

Tracer


People also ask

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.

What does make_shared return?

std::make_shared 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 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 happens if make_shared fails?

So, if you throw exception from your class' constructor, then std::make_shared will throw it too. Besides exceptions thrown from constructor, std::make_shared could throw std::bad_alloc exception on its own. Therefore, you don't need to check if the result of std::make_shared is nullptr .


2 Answers

The first case does not perform a double allocation, it performs two allocations, one for the managed object and one for the control block of the shared_ptr.

For the second case, cppreference has a good explanation for why std::make_shared usually only performs one memory allocation it says (emphasis mine going forward):

This function typically allocates memory for the T object and for the shared_ptr's control block with a single memory allocation (it is a non-binding requirement in the Standard). In contrast, the declaration std::shared_ptr p(new T(Args...)) performs at least two memory allocations, which may incur unnecessary overhead.

and from std::shared_ptr section it says:

When shared_ptr is created by calling std::make_shared or std::allocate_shared, the memory for both the control block and the managed object is created with a single allocation. The managed object is constructed in-place in a data member of the control block. When shared_ptr is created via one of the shared_ptr constructors, the managed object and the control block must be allocated separately. In this case, the control block stores a pointer to the managed object.

This make_shared description is consistent with the C++11 draft standard which says in section 20.7.2.2.6 shared_ptr creation

template<class T, class... Args> shared_ptr<T> make_shared(Args&&... args); template<class T, class A, class... Args>   shared_ptr<T> allocate_shared(const A& a, Args&&... args); 

[...]

Remarks: Implementations should perform no more than one memory allocation. [ Note: This provides efficiency equivalent to an intrusive smart pointer. —end note ]

[ Note: These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. —end note ]

Herb Sutter has a more detailed explanation of the advantages of using make_shared in GotW #89 Solution: Smart Pointers and points out some advantages:

  • It reduces allocation overhead
  • It improves locality.
  • Avoids an explicit new.
  • Avoids an exception safety issue.

Be aware that when using std::weak_ptr using make_shared has some disadvantages.

like image 187
Shafik Yaghmour Avatar answered Sep 24 '22 08:09

Shafik Yaghmour


Explanation from cppreference std::shared_ptr in Implementation notes section

In a typical implementation, std::shared_ptr holds only two pointers:

  1. a pointer to the managed object
  2. a pointer to control block

When shared_ptr is created by calling std::make_shared or std::allocate_shared, the memory for both the control block and the managed object is created with a single allocation. The managed object is constructed in-place in a data member of the control block. When shared_ptr is created via one of the shared_ptr constructors, the managed object and the control block must be allocated separately. In this case, the control block stores a pointer to the managed object.

like image 20
Alper Avatar answered Sep 20 '22 08:09

Alper