只是澄清一下,使用make_unique
只在表达式中有多个分配时才增加异常安全性,而不仅仅是一个,是否正确?例如
void f(T*);
f(new T);
是完全异常安全的(就分配和东西而言),而
void f(T*, T*);
f(new T, new T);
不是,对吗?
答案 0 :(得分:23)
不仅在您有多个分配时,而且每当您可以在不同的地方投掷时。考虑一下:
f(make_unique<T>(), function_that_can_throw());
对战:
f(unique_ptr<T>(new T), function_that_can_throw());
在第二种情况下,允许编译器调用(按顺序):
new T
function_that_can_throw()
unique_ptr<T>(...)
显然如果function_that_can_throw
实际抛出,那么你会泄漏。 make_unique
阻止了这种情况。
当然,第二次分配(如你的问题所示)只是function_that_can_throw()
的一个特例。
作为一般经验法则,只需使用make_unique
即可使代码保持一致。当你需要一个unique_ptr
时它总是正确的(阅读:异常安全),它对性能没有任何影响,所以没有理由不使用它(实际上不是使用它会引入很多陷阱。
答案 1 :(得分:6)
我认为你最好使用std::unique_ptr<T>
来比较实际情况:
void f(std::unique_ptr<T>);
f(std::unique_ptr<T>(new T));
f(std::make_unique<T>());
如果抛出异常,这些调用都不会泄漏。然而
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>());
在这种情况下,如果抛出异常,显式使用std::unique_ptr<T>
的版本可能会泄漏(因为编译器可能会在构造任何临时值之前开始评估new
- 表达式。)
答案 2 :(得分:5)
从C ++ 17开始,异常安全问题由[expr.call]
的重写修复参数的初始化,包括每个相关的值计算和副作用,都是相对于任何其他参数的初始化顺序排列的。
这里不确定地排序意味着一个在另一个之前排序,但是没有指定哪个。
f(unique_ptr<T>(new T), function_that_can_throw());
只能有两种可能的执行顺序
new T
unique_ptr<T>::unique_ptr
function_that_can_throw
function_that_can_throw
new T
unique_ptr<T>::unique_ptr
这意味着它现在是异常安全的。