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.
Difference in make_shared and normal shared_ptr in C++
The difference is that std::make_shared
performs one heap-allocation, whereas calling the std::shared_ptr
constructor performs two.
Where do the heap-allocations happen?
std::shared_ptr
manages two entities:
- the control block (stores meta data such as ref-counts, type-erased deleter, etc)
- the object being managed
std::make_shared
performs a single heap-allocation accounting for the space necessary for both the control block and the data. In the other case, new Obj("foo")
invokes a heap-allocation for the managed data and the std::shared_ptr
constructor performs another one for the control block.
For further information, check out the implementation notes at cppreference.
Update I: Exception-Safety
NOTE (2019/08/30): This is not a problem since C++17, due to the changes in the evaluation order of function arguments. Specifically, each argument to a function is required to fully execute before evaluation of other arguments.
Since the OP seem to be wondering about the exception-safety side of things, I've updated my answer.
Consider this example,
void F(const std::shared_ptr<Lhs> &lhs, const std::shared_ptr<Rhs> &rhs) { /* ... */ }
F(std::shared_ptr<Lhs>(new Lhs("foo")),
std::shared_ptr<Rhs>(new Rhs("bar")));
Because C++ allows arbitrary order of evaluation of subexpressions, one possible ordering is:
new Lhs("foo"))
new Rhs("bar"))
std::shared_ptr<Lhs>
std::shared_ptr<Rhs>
Now, suppose we get an exception thrown at step 2 (e.g., out of memory exception, Rhs
constructor threw some exception). We then lose memory allocated at step 1, since nothing will have had a chance to clean it up. The core of the problem here is that the raw pointer didn't get passed to the std::shared_ptr
constructor immediately.
One way to fix this is to do them on separate lines so that this arbitary ordering cannot occur.
auto lhs = std::shared_ptr<Lhs>(new Lhs("foo"));
auto rhs = std::shared_ptr<Rhs>(new Rhs("bar"));
F(lhs, rhs);
The preferred way to solve this of course is to use std::make_shared
instead.
F(std::make_shared<Lhs>("foo"), std::make_shared<Rhs>("bar"));
Update II: Disadvantage of std::make_shared
Quoting Casey's comments:
Since there there's only one allocation, the pointee's memory cannot be deallocated until the control block is no longer in use. A
weak_ptr
can keep the control block alive indefinitely.
Why do instances of weak_ptr
s keep the control block alive?
There must be a way for weak_ptr
s to determine if the managed object is still valid (eg. for lock
). They do this by checking the number of shared_ptr
s that own the managed object, which is stored in the control block. The result is that the control blocks are alive until the shared_ptr
count and the weak_ptr
count both hit 0.
Back to std::make_shared
Since std::make_shared
makes a single heap-allocation for both the control block and the managed object, there is no way to free the memory for control block and the managed object independently. We must wait until we can free both the control block and the managed object, which happens to be until there are no shared_ptr
s or weak_ptr
s alive.
Suppose we instead performed two heap-allocations for the control block and the managed object via new
and shared_ptr
constructor. Then we free the memory for the managed object (maybe earlier) when there are no shared_ptr
s alive, and free the memory for the control block (maybe later) when there are no weak_ptr
s alive.
Does shared_ptr a = make_shared() create a copy of the shared_ptr before constructor is run?
There are two things to avoid the copy:
- 1 is the compiler's RVO (return value optimization);
- 2 is the move constructor/assignment.
for code auto foo = std::make_shared<Foo>();
RVO will create the object directly on the stack. and even we disable the RVO by -fno-elide-constructors
, the move constructor will try used as the returned object from make_shared
is a temporary one.
Below is a simple test code. (this code only show the concept but not for a real-world shared_ptr implementation)
#include <iostream>
template <typename T>
struct my_shared_ptr
{
T *t_{nullptr};
my_shared_ptr(T *t): t_(t) {
std::cout << "constructor" << std::endl;
};
my_shared_ptr(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
}
my_shared_ptr<T>& operator=(const my_shared_ptr<T>&) {
std::cout << "copy" << std::endl;
return *this;
}
#ifndef NO_MOVE
my_shared_ptr(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
}
my_shared_ptr<T>& operator=(my_shared_ptr<T>&&) {
std::cout << "move" << std::endl;
return *this;
}
#endif
};
template <typename T>
my_shared_ptr<T>
my_make_shared() {
return my_shared_ptr<T>(new T);
}
struct Foo {};
int main()
{
auto foo = my_make_shared<Foo>();
return 0;
}
Condition 1, compile with c++11 shows:
$ g++ a.cc -std=c++11 ; ./a.out
constructor
Condition 2, compile with c++11/disable RVO shows:
$ g++ a.cc -std=c++11 -fno-elide-constructors ; ./a.out
constructor
move
move
Condition 3, compile with c++11/disable RVO/no move shows:
$ g++ a.cc -std=c++11 -fno-elide-constructors -DNO_MOVE ; ./a.out
constructor
copy
copy
Initializing shared_ptr member variable, new vs make_shared?
The only times when make_shared
is not allowed are:
- If you're getting a naked pointer allocated by someone else and storing it in
shared_ptr
. This is often the case when interfacing with C APIs. If the constructor you want to call is not public (
make_shared
can only call public constructors). This can happen with factory functions, where you want to force users to create the object from the factory.However, there are ways to get around this. Instead of having a private constructor, have a public constructor. But make the constructor take a type with can only be constructed by those with private access to the class. That way, the only people who can call
make_shared
with that object type are those with private access to the class.
So yes, you can do this.
std::make_shared leads to undefined behavior, but new works
Your call to make_shared
is using a copy constructor for your Foo
class, which you haven't defined. Thus, the default (compiler-generated) copy will be used, and the destructor will be called to delete the temporary. As your class doesn't properly implement the Rule of Three, this (potentially) causes undefined behaviour.
The copy constructor is being used because the argument list in your call to std::make_shared
is Foo(3, false)
– a Foo
object, so the call fits only the first overload listed on this cppreference page. (Note that there is nothing resembling a std::make_shared<T>(const T& src)
overload.) From that page, we see:
- Constructs an object of type T and wraps it in a std::shared_ptr
using args as the parameter list for the constructor of T.
So, with your "args" of Foo(3, false)
, that make_shared
actually calls the following constructor for the object to wrap:
Foo(Foo(3, false))
For correct behaviour, just pass the 3
and false
as arguments to make_shared
:
std::shared_ptr<Foo> sp_foo = std::make_shared<Foo>(3, false);
You can demonstrate the error in your original code by adding a destructor to the Foo
class that logs some output, like: ~Foo() { std::cout << "Destroying...\n"; }
. You will see that output after execution of the call to make_shared
.
You can prevent this accidental error/oversight by deleting the Foo
copy constructor: Foo(const Foo& f) = delete;
. This will generate a compiler error along the following lines:
error : call to deleted constructor of 'Foo'
However, in your second case, you use a std::shared_ptr
constructor (the third form shown on the linked page). This has no problem, because that constructor 'just' wraps the given pointer into the managed object, and no copying or destruction is needed.
How to initialize shared_ptr not knowing its type?
Assuming that sometypePtr
is a non-array std::shared_ptr
, then you can use sometypePtr::element_type
.
template<typename sometypePtr>
void foo()
{
sometypePtr ptr = std::make_shared<typename sometypePtr::element_type>();
}
If sometypePtr
is an array std::shared_ptr
, you will have to supply the extent as well as the type.
no matching function for call to 'make_shared'
The error message is complaining you using an prvalue 10
. Try to use
int avar = 10;
auto foo = std::make_shared<int> (avar);
It is interesting to see what happens when using a lvalue.
Did you build the std library locally? If you are, maybe you could try to rebuild again or grab a prebuilt library from somewhere.
I tested the code on https://godbolt.org/ with configuration x86-64 clang 7.0.0
and -std=c++11
. it works fine. Even you using iOS, it should be good on that os I guess.
I also see you using -Wc++11-extensions
when you building. Try use -std=c++11
instead maybe?
Edit by DG: As noted in my comment below, this last suggestion, "Try use -std=c++11
," worked! With -std=c++11
then all values (lvalues, rvalues, prvalues, etc.) work fine. See comments below.
Why use std::make_unique in C++17?
You're right that the main reason was removed. There are still the don't use new guidelines and that it is less typing reasons (don't have to repeat the type or use the word new
). Admittedly those aren't strong arguments but I really like not seeing new
in my code.
Also don't forget about consistency. You absolutely should be using make_shared
so using make_unique
is natural and fits the pattern. It's then trivial to change std::make_unique<MyClass>(param)
to std::make_shared<MyClass>(param)
(or the reverse) where the syntax A requires much more of a rewrite.
Differences between std::make_unique and std::unique_ptr with new
The motivation behind make_unique
is primarily two-fold:
make_unique
is safe for creating temporaries, whereas with explicit use ofnew
you have to remember the rule about not using unnamed temporaries.foo(make_unique<T>(), make_unique<U>()); // exception safe
foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*The addition of
make_unique
finally means we can tell people to 'never' usenew
rather than the previous rule to "'never' usenew
except when you make aunique_ptr
".
There's also a third reason:
make_unique
does not require redundant type usage.unique_ptr<T>(new T())
->make_unique<T>()
None of the reasons involve improving runtime efficiency the way using make_shared
does (due to avoiding a second allocation, at the cost of potentially higher peak memory usage).
* It is expected that C++17 will include a rule change that means that this is no longer unsafe. See C++ committee papers P0400R0 and P0145R3.
How do I call ::std::make_shared on a class with only protected or private constructors?
This answer is probably better, and the one I'll likely accept. But I also came up with a method that's uglier, but does still let everything still be inline and doesn't require a derived class:
#include <memory>
#include <string>
class A {
protected:
struct this_is_private;
public:
explicit A(const this_is_private &) {}
A(const this_is_private &, ::std::string, int) {}
template <typename... T>
static ::std::shared_ptr<A> create(T &&...args) {
return ::std::make_shared<A>(this_is_private{0},
::std::forward<T>(args)...);
}
protected:
struct this_is_private {
explicit this_is_private(int) {}
};
A(const A &) = delete;
const A &operator =(const A &) = delete;
};
::std::shared_ptr<A> foo()
{
return A::create();
}
::std::shared_ptr<A> bar()
{
return A::create("George", 5);
}
::std::shared_ptr<A> errors()
{
::std::shared_ptr<A> retval;
// Each of these assignments to retval properly generates errors.
retval = A::create("George");
retval = new A(A::this_is_private{0});
return ::std::move(retval);
}
Edit 2017-01-06: I changed this to make it clear that this idea is clearly and simply extensible to constructors that take arguments because other people were providing answers along those lines and seemed confused about this.
Related Topics
Equivalent C++ to Python Generator Pattern
Downcasting Shared_Ptr<Base> to Shared_Ptr<Derived>
Is Rvo (Return Value Optimization) Applicable for All Objects
Why Destructor Is Not Called on Exception
Why Do We Need to Use 'Int Main' and Not 'Void Main' in C++
Doing a Static_Assert That a Template Type Is Another Template
C++ Equivalent to Designated Initializers
Creating a Counter That Stays Synchronized Across Mpi Processes
Are "Seekp" & "Seekg" Interchangeable
C++ - Decimal to Binary Converting
What Does Lpcwstr Stand for and How Should It Be Handled With
How to Download the Visual C++ Command Line Compiler Without Visual Studio
Are There Any Tricks to Use Std::Cin to Initialize a Const Variable
Converting Std::String to Std::Vector<Char>
What Is an Opaque Value in C++