Skip to content

Prefer use object generator

1、在 Exception-Safe-Function-Call 章节中讨论了function call的order of evaluation 和 exception safety、resource leak之间的问题

2、CppCoreGuidelines R: Resource management # Allocation and deallocation rule summary:

a、R.11: Avoid calling new and delete explicitly

b、R.13: Perform at most one explicit resource allocation in a single expression statement

stackoverflow Exception safety and make_unique

Just to clarify, using make_unique only adds exception safety when you have multiple allocations in an expression, not just one, correct? For example

void f(T*);

f(new T);

is perfectly exception safe (as far as allocations and stuff), while

void f(T*, T*);

f(new T, new T);

is not, correct?

NOTE: 提问者所问的是: code1是exception safe而code2不是exception safe。

Comments

multiple allocations in an expression but each of those allocations via new T is a separate expression, so you have the opposite: one allocation per each of multiple expressions. That the function call is itself an expression containing the other 2 doesn't change that. But aside from the wording, you were on the right track. By my reading, the rule, as formulated by Sutter, is to perform each allocation within its own statement or sequence them by returning from a separate function call, thereby avoiding weird ordering & leaks: gotw.ca/gotw/056.htm (oldie-but-goodie) – underscore_d Aug 13 '16 at 11:25

NOTE: 这个comment是非常好的,gotw.ca/gotw/056.htm (oldie-but-goodie) 是非常好的文章,已经收录了。

A

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().

NOTE: 这段话其实所说的是这种情况: g(std::unique_ptr<T>(new T), std::unique_ptr<T>(new T));

一旦第二个std::unique_ptr<T>(new T) throw,则可能leak。

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).

Comments

Exception safety is not (just) about "no exceptions" but rather about "guaranteeing the right behaviour when you get an exception". The make_unique documentation is clear: no effect if there is an exception, this is a strong guarantee (see en.wikipedia.org/wiki/Exception_safety). And the unique_ptr is not yet constructed so, again, no effect; the exception is simply propagated. – syam Sep 24 '20 at 19:18

NOTE: 实现了strong exception safety

A

As of C++17, the exception safety issue is fixed by a rewording of [expr.call]

The initialization of a parameter, including every associated value computation and side effect, is indeterminately(不确定的) sequenced with respect to that of any other parameter.

Here indeterminately sequenced means that one is sequenced before another, but it is not specified which.

f(unique_ptr<T>(new T), function_that_can_throw());

Can have only two possible order of execution

  1. new T unique_ptr<T>::unique_ptr function_that_can_throw
  2. function_that_can_throw new T unique_ptr<T>::unique_ptr

Which means it is now exception safe.

A

NOTE: 非常好的总结

I'd think you'd be better off comparing things actually using std::unique_ptr<T>:

void f(std::unique_ptr<T>);

f(std::unique_ptr<T>(new T));
f(std::make_unique<T>());

Neither of these calls can leak if there is an exception being thrown.

NOTE:

1、上述都不会leak?

However

void f(std::unique_ptr<T>, std::unique_ptr<T>);

g(std::unique_ptr<T>(new T), std::unique_ptr<T>(new T));
g(std::make_unique<T>(), std::make_unique<T>());

In this case, the version using std::unique_ptr<T> explicitly can leak if an exception is thrown (because the compiler might start evaluating the new-expressions before constructing either of the temporaries).

stackoverflow Differences between std::make_unique and std::unique_ptr with new

Does std::make_unique have any efficiency benefits like std::make_shared?

Compared to manually constructing std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));

A

The motivation behind make_unique is primarily two-fold:

1、make_unique is safe for creating temporaries, whereas with explicit use of new you have to remember the rule about not using unnamed temporaries.

NOTE: 上述"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*

2、The addition of make_unique finally means we can tell people to 'never' use new rather than the previous rule to "'never' use new except when you make a unique_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.

NOTE: 在前面的 stackoverflow Exception safety and make_unique # A 中已经对此进行了解释。

A

std::make_unique and std::make_shared are there for two reasons:

1、So that you don't have to explicitly list the template type arguments.

2、Additional exception safety over using std::unique_ptr or std::shared_ptr constructors. (See the Notes section here.)

It's not really about runtime efficiency. There is the bit about the control block and the T being allocated all at once, but I think that's more a bonus and less a motivation for these functions to exist.

stackoverflow Advantages of using std::make_unique over new operator [duplicate]

A

Advantages

1、make_unique teaches users "never say new/delete and new[]/delete[]" without disclaimers.

NOTE: 翻译如下:

“make_unique”教给用户“在没有免责声明的情况下绝不说‘new’/‘delete’和‘new[]’/‘delete[]’”。

2、make_unique shares two advantages with make_shared (excluding(除了) the third advantage, increased efficiency).

First, unique_ptr<LongTypeName> up(new LongTypeName(args)) must mention LongTypeName twice, while auto up = make_unique<LongTypeName>(args) mentions it once.

3、make_unique prevents the unspecified-evaluation-order leak triggered by expressions like foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)). (Following the advice "never say new" is simpler than "never say new, unless you immediately give it to a named unique_ptr".)

4、make_unique is carefully implemented for exception safety and is recommended over directly calling unique_ptr constructors.

NOTE: 提高exception safety

When not to use make_unique

  • Don't use make_unique if you need a custom deleter or are adopting a raw pointer from elsewhere.

Sources

1、Proposal of std::make_unique.

2、Herb Sutter's GotW #89 Solution: Smart Pointers

When use raw new?

stackoverflow Differences between std::make_unique and std::unique_ptr with new # A

A reason why you would have to use std::unique_ptr(new A()) or std::shared_ptr(new A()) directly instead of std::make_*() is being unable to access the constructor of class A outside of current scope.

stackoverflow Differences between std::make_unique and std::unique_ptr with new # A # comment

One reason I once had to use std::unique_ptr<T>(new T()) was because the constructor of T was private. Even if the call to std::make_unique was in a public factory method of class T, it didn't compile because one of the underlying methods of std::make_unique could not access the private constructor. I didn't want to make that method friend because I didn't want to rely on the implementation of std::make_unique. So the only solution was, calling new in my factory method of class T, and then wrap it in an std::unique_ptr<T>. – Patrick Jun 5 '19 at 7:09