27

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?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Tracer
  • 2,544
  • 2
  • 23
  • 58
  • 6
    To be clear, it's not a double allocation of the `int`. It's just two separate allocations: one for the `int` object and another for the `shared_ptr` control block. The second line is only a single allocation of both the `int` and the control block in one go. – Joseph Mansfield Jul 16 '14 at 11:47
  • 2
    see point 2 here: http://herbsutter.com/2013/05/29/gotw-89-solution-smart-pointers/ – perreal Jul 16 '14 at 11:48
  • 1
    In the second case `make_shared` allocates both the `int` and the control block and is therefore free to allocate both in one go. In the first case you allocate the `int` and the constructor of `shared_ptr` allocates the control block and there is no way to unite the allocations. – nwp Jul 16 '14 at 11:49
  • 1
    N.B. there is no guarantee that `make_shared` only does one allocation, it's just _possible_ for the implementation to do a single allocation ... but if your implementation doesn't perform that optimization you should complain to your vendor (at least the GCC, LLVM and MSVC implementations do it, and `boost::make_shared` does too) – Jonathan Wakely Jul 16 '14 at 12:24
  • Here is another decent explanation of what is going on under the hood: http://stackoverflow.com/a/8646062/576911 – Howard Hinnant Aug 13 '14 at 18:09
  • duplicate of [Difference in make\_shared and normal shared\_ptr in C++](https://stackoverflow.com/questions/20895648/difference-in-make-shared-and-normal-shared-ptr-in-c) – underscore_d Jul 13 '17 at 18:13

3 Answers3

32

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.

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
4

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.

Alper
  • 12,860
  • 2
  • 31
  • 41
1

There is also a potential subtle bug: in sp(new int) you fist allocate an int (whose pointer is given to sp), than sp itself has to allocate a control block (will contain the counters and the deleter).

Now, if doing this last allocation sp fails (low memory) you are left with a heap allocated int whose pointer is not held by anyone, and thus impossible to delete. (memory leak).

Emilio Garavaglia
  • 20,229
  • 2
  • 46
  • 63