Exception safety and make_unique
Not only when you have multiple allocations, but whenever you can throw at different places. Consider this:
f(make_unique<T>(), function_that_can_throw());
Versus:
f(unique_ptr<T>(new T), function_that_can_throw());
In the second case, the compiler is allowed to call (in order):
new T
function_that_can_throw()
unique_ptr<T>(...)
Obviously if function_that_can_throw
actually throws then you leak. make_unique
prevents this case.
And of course, a second allocation (as in your question) is just a special case of function_that_can_throw()
.
As a general rule of thumb, just use make_unique
so that your code is consistent. It is always correct (read: exception-safe) when you need a unique_ptr
, and it doesn't have any impact on performance, so there is no reason not to use it (while actually not using it introduces a lot of gotchas).
Exceptionsafety of make_unique: Why is f(new T) exception safe
The reason is that in a function call or similar, the arguments do not induce sequence points (are not "sequenced before"). For example:
do_work(unique_ptr<A>(new A), unique_ptr<B>(new B));
The compiler is allowed to generate code that looks like:
new A
new B
// might throw!- Construct
unique_ptr<A>
- Construct
unique_ptr<B>
- Call
do_work
If new B
throws, then you've leaked the A
, because no unique_ptr
was ever constructed.
Putting the unique_ptr
construction into its own function eliminates this problem, because compilers aren't allowed to execute function bodies concurrently (so the "new" and "construct unique_ptr
steps need to be done together).
That is, given:
do_work(make_unique<A>(), make_unique<B>())
The compiler must generate code that looks like:
- Call
make_unique<A>
- Call
make_unique<B>
- Call
do_work
or
- Call
make_unique<B>
- Call
make_unique<A>
- Call
do_work
making the leak where there are new'd objects floating around without owning unique_ptr
s not possible.
std::unique_ptr and exception safety
- All of
std::unique_ptr
's constructors* arenoexcept
malloc
won't throw any exception on failure... it will just returnnullptr
.- I believe your deleter's constructors won't throw anything either.
So you don't need to catch anything, since nothing will be thrown.
*: See C++11 §20.7.1.2.1 unique_ptr
constructors [unique.ptr.single.ctor]
How does use of make_unique prevent memory-leak, in C++?
How does use of make_unique prevent memory-leak, in C++?
std::make_unique
doesn't "prevent" memory-leak in the sense that it's still possible to write memory leaks in programs that use std::make_unique
.
What does std::make_unique
is make it easier to write programs that don't have memory leaks.
A or B gets leaked if an exception is thrown there.
Why?
Pre C++17:
Because if you allocate A
, then call constructor of B
before constructing the std::unique_ptr
that was supposed to own A
, and the constructor of B
throws, then A
will leak (or same happens with A
and B
reversed).
Since C++17:
There's no leak since the scenario described above cannot happen anymore in the shown example.
What does make_unique do that the default unique_ptr does not?
std::make_unique
allocates memory, and either successfully returns a valid std::unique_ptr
, or throws an exception without leaking memory.
std::unique_ptr(T*)
accepts a pointer that was allocated separately. If an exception is thrown before the constructor is called, then there will never have been a unique pointer owning the allocation.
It's possible to fix the (pre-C++17) bug without using std::make_unique
:
auto a = std::unique_ptr<A>{new A{}};
auto b = std::unique_ptr<B>{new B{}};
fn(std::move(a), std::move(b));
But if you always use std::make_unique
, then you won't accidentally make the mistake of writing leaky version. Furthermore, std::make_unique
lets you avoid writing new
which allows you to use the rule of thumb "write exactly one delete for each new". 0 new -> 0 delete.
Why does C++ need std::make_unique over forwarded unique_ptr constructor?
I want to summarize discussion with Some programmer dude, StoryTeller - Unslander Monica and Raymond Chen
So, there are 2 reasons:
std::make_unique
pairs well with std::make_shared which was introduced earlier so this was easier to learn than new constructor forunique_ptr
.- There is possible ambiguity between constructor of
unique_ptr
and constructor of inner value type (T
) if this type have own constructor which takes a pointer to self type. E.g.
struct Inner{
Inner() = default;
Inner(Inner* parent_node): parent(parent_node){}
Inner* parent = nullptr;
};
Inner* parent = make_parent();
// It would be not clear for human which constructor must be called here
std::unique_ptr<Inner> child(parent);
Compiler can deduce which constructor should be called here but it is hard for human. So having function std::make_unique
is beneficial because it is clear that all constructors of std::unique_ptr
create only unique_ptr and never call inner value constructor while std::make_unique
would always call constructor of inner value. This makes code much easier to reason about.
Thanks to everyone for discussion!
Shared_ptr and unique_ptr with exception
Let's look into example as how std::unique_ptr
can be used for providing exception safety:
someclass *ptr = new someclass;
...
delete ptr; // in case of exception we have problem
so instead we should use:
std::unique_ptr<someclass> ptr = std::make_unique<someclass>();
... // no problem
simple, safe and no overhead.
So can shared_ptr
be used same way to provide exception safety? Yes it can. But it should not, as it is designed for different purpose and would have unnecessary overhead. So it is not mentioned as a tool for such cases, but it does not mean it would not delete owned object if it is the only owner.
Related Topics
How Will I Know Whether Inline Function Is Actually Replaced at the Place Where It Is Called or Not
How to Tell If the C Function Atoi Failed or If It Was a String of Zeros
How to Make a Portable Isnan/Isinf Function
The Intersection of Two Sorted Arrays
Why Doesn't Std::String Provide Implicit Conversion to Char*
What Is the Proper Way of Doing Event Handling in C++
When Is a Type in C++11 Allowed to Be Memcpyed
Why Do Un-Named C++ Objects Destruct Before the Scope Block Ends
Populate an Array Using Constexpr at Compile-Time
How to Read Groups of Integers from a File, Line by Line in C++
What's a Good Hash Function for English Words
How to Change the Background Color of a Button Winapi C++
Can Class Template Constructors Have a Redundant Template Parameter List in C++20
What Does "Memory Allocated at Compile Time" Really Mean