Is make_shared really more efficient than new?
As infrastructure I was using llvm/clang 3.0 along with the llvm std c++ library within XCode4.
Well that appears to be your problem. The C++11 standard states the following requirements for make_shared<T>
(and allocate_shared<T>
), in section 20.7.2.2.6:
Requires: The expression ::new (pv) T(std::forward(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.
T
is not required to be copy-constructable. Indeed, T
isn't even required to be non-placement-new constructable. It is only required to be constructable in-place. This means that the only thing that make_shared<T>
can do with T
is new
it in-place.
So the results you get are not consistent with the standard. LLVM's libc++ is broken in this regard. File a bug report.
For reference, here's what happened when I took your code into VC2010:
Create smart_ptr using make_shared...
Constructor make_shared
Create smart_ptr using make_shared: done.
Create smart_ptr using new...
Constructor new
Create smart_ptr using new: done.
Destructor
Destructor
I also ported it to Boost's original shared_ptr
and make_shared
, and I got the same thing as VC2010.
I'd suggest filing a bug report, as libc++'s behavior is broken.
std::shared_ptr initialization: make_sharedFoo() vs shared_ptrT(new Foo)
Both examples are rather more verbose than necessary:
std::shared_ptr<int> p(new int); // or '=shared_ptr<int>(new int)' if you insist
auto p = std::make_shared<int>(); // or 'std::shared_ptr<int> p' if you insist
What's the difference?
The main difference is that the first requires two memory allocations: one for the managed object (new int
), and one for the reference count. make_shared
should allocate a single block of memory, and create both in that.
Which one should I prefer and why?
You should usually use make_shared
as it's more efficient. As noted in another answer, it also avoids any possibility of a memory leak, since you never have a raw pointer to the managed object.
However, as noted in the comments, it has a potential disadvantage that the memory won't be released when the object is destroyed, if there are still weak pointers preventing the shared count from being deleted.
EDIT 2020/03/06:
Further recommendations come also from the official Microsoft documentation with associated examples. Keep the focus on the Example 1 snippet:
Whenever possible, use the make_shared function to create a shared_ptr
when the memory resource is created for the first time. make_shared is
exception-safe. It uses the same call to allocate the memory for the
control block and the resource, which reduces the construction
overhead. If you don't use make_shared, then you have to use an
explicit new expression to create the object before you pass it to the
shared_ptr constructor. The following example shows various ways to
declare and initialize a shared_ptr together with a new object.
new and make_shared for shared pointers
The code referred to as the second variable is in fact this (taken from OP's code):
auto ptr_res2(new Object("new"));
This does not create a
std::shared_ptr
, it creates a pointer toObject
.When creating a
std::shared_ptr
using its constructor that takes a naked pointer, you must pass a pointer to already allocated memory (e.g. allocated usingnew
). This means that the memory for the object has already been allocated when creating thestd::shared_ptr
object itself. Thestd::shared_ptr
needs to allocate memory for its own workings, like e.g. a reference counter. So there are 2 allocations: the one usingnew
passed to the ctor ofstd::shared_ptr
and the one required when constructing thestd::shared_ptr
itself.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 allocation as you pass it the arguments needed to construct the object, not a pointer to the object itself.std::make_shared
can then allocate memory once that holds both the object and the ref counter.auto p2 = std::make_shared<Object>{"Foo"} // Allocation 1 (object & counter).
When should I prefer `shared_ptr` to `make_shared`?
One of the situations is that std::make_shared does not support the specifying of the custom deleter.
Unlike the
std::shared_ptr
constructors,std::make_shared
does not allow a custom deleter.
You can only do it with the constructor of std::shared_ptr, e.g.
std::shared_ptr<Foo> sh5(new Foo, [](auto p) {
std::cout << "Call delete from lambda...\n";
delete p;
});
Another issue is just as the linked post explained, std::make_shared
performs only one allocation for both the control block and the object pointed to. That means after the object being destroyed, the memory it occupied might not be deallocated immediately. That might cause some memory usage issue.
How to create pointer with `make_shared`
You seem to be missing the namespace from your second example. Also you can construct your derived type in make_shared
.
boost::shared_ptr<Exercise> americanExercise = boost::make_shared<AmericanExercise>(settlementDate, in.maturity);
Data cache implications of using std::make_shared()
Maybe, but don't count on it.
For cache-friendliness, you want to use as little memory as possible, and you want operations that are close together in address to also be close together in time (that is, close enough that the second operation uses memory that is still in some level of cache from the effects of the first operation: the lower the level of cache the better).
If you use make_shared
, then there might well be a slight saving in total memory use, which at least tends to be a win for the cache no matter what your memory usage pattern.
If you use make_shared
, then the control block and the object referred to (referand) will be adjacent in memory.
If you don't use make_shared
, and your objects are a different size from your control blocks, then with common memory allocators there's a reasonable chance that the objects will be clustered together in one place and the control blocks clustered together in a different place. If they are the same size (once rounded by the memory allocator in some implementation-specific way), then with common memory allocators there's a reasonable chance that they'll just alternate in memory for long runs unless shared_ptr
does something to affect that.
Your memory access pattern will determine which of those layouts is better for the cache -- and of course the actual layout you get in the non-make_shared
case might be something else again, depending on implementation details.
The fact that you have a vector
is basically independent of all this, since the shared_ptr
objects are separate from the control-blocks and the referands.
make_shared() reference counting in C++
A shared pointer contains two parts: The pointer to the "object" you have created, and a pointer to a special control block that contains the reference counter and possibly some other meta-data needed.
If you create your own std::shared_ptr
object, these two memory block will be allocated separately. If you use std::make_shared
then the function will only make a single allocation for both blocks of memory.
Related Topics
Restrict Variadic Template Arguments
C++ Template Typename Iterator
C++11 Variable Number of Arguments, Same Specific Type
Boost-Python How to Pass a C++ Class Instance to a Python Class
Is C++ Static Member Variable Initialization Thread-Safe
Std::Forward_List and Std::Forward_List::Push_Back
C++ Inheritance and Member Function Pointers
Overload Resolution with Ref-Qualifiers
Changing Dpi Scaling Size of Display Make Qt Application's Font Size Get Rendered Bigger
Add External Libraries to Cmakelist.Txt C++
Is Returning References of Member Variables Bad Practice
Project Error: Unknown Module(S) in Qt: Webkitwidgets
Are There Any Downsides to Using Upx to Compress a Windows Executable
How to Rotate a N X N Matrix by 90 Degrees