std :: make_unique和std :: unique_ptr之间的差异与new

NFR*_*RCR 118 c++ smart-pointers unique-ptr c++11 c++14

是否std::make_unique有像任何效率优势std::make_shared

与手动构建相比std::unique_ptr:

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(1));
Run Code Online (Sandbox Code Playgroud)

bam*_*s53 134

背后的动机make_unique主要是双重的:

  • make_unique创造临时工作是安全的,而明确使用new你必须记住不使用未命名临时工的规则.

    foo(make_unique<T>(), make_unique<U>()); // exception safe
    
    foo(unique_ptr<T>(new T()), unique_ptr<U>(new U())); // unsafe*
    
    Run Code Online (Sandbox Code Playgroud)
  • 添加make_unique最终意味着我们可以告诉人们"从不"使用new而不是先前的规则"永远不会"使用,new除非你做了unique_ptr".

还有第三个原因:

  • make_unique不需要冗余类型使用.unique_ptr<T>(new T())- >make_unique<T>()

没有任何原因涉及使用方式提高运行时效率make_shared(由于避免了第二次分配,代价是可能更高的峰值内存使用).

*预计C++ 17将包含规则更改,这意味着这不再是不安全的.参见C++委员会论文P0400R0P0145R3.

  • 实际上,从http://stackoverflow.com/a/19472607/368896,我得到了......从那个答案,考虑以下函数调用`f`:`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`可以防止这种情况.*所以,我的问题得到了回答. (12认同)
  • @TimothyShields是的,这就是我的意思。只是在C ++ 11中,我们有`make_shared`,所以`make_unique`是以前丢失的最后一部分。 (2认同)
  • 您可以通过什么方式简要提及或链接到不使用未命名临时变量的原因? (2认同)
  • 我曾经不得不使用std :: unique_ptr &lt;T&gt;(new T())的原因之一是因为T的构造函数是私有的。即使对std :: make_unique的调用是在类T的公共工厂方法中,它也无法编译,因为std :: make_unique的基础方法之一无法访问私有构造函数。我不想让该方法成为朋友,因为我不想依赖于std :: make_unique的实现。因此,唯一的解决方案是,在我的类T的工厂方法中调用new,然后将其包装在std :: unique_ptr &lt;T&gt;中。 (2认同)

Tim*_*lds 12

std::make_uniquestd::make_shared在那里有两个原因:

  1. 这样您就不必显式列出模板类型参数.
  2. 使用std::unique_ptrstd::shared_ptr构造函数的额外异常安全性.(参见此处的注释部分.)

它并不是关于运行时效率的.有一点关于控制块和T一次性分配,但我认为这是一个奖励,而不是这些功能存在的动机.


Vol*_*hko 7

您必须使用std::unique_ptr(new A())std::shared_ptr(new A())直接替代的std::make_*()原因是无法访问A当前范围之外的类的构造函数.

  • @nyanpasu64 make_unique 不需要修复。您始终可以使用私有构造函数使其成为您的类的友元。 (3认同)

小智 6

考虑函数调用

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }
Run Code Online (Sandbox Code Playgroud)

假设new A()成功,但new B()抛出异常:您捕获它以恢复程序的正常执行。不幸的是,C++ 标准不要求A销毁对象并释放其内存:内存会悄悄泄漏,并且无法清理它。通过将A和包装Bstd::make_unique()调用中,您可以确定不会发生泄漏:

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }
Run Code Online (Sandbox Code Playgroud)

这里的要点是std::make_unique<A>(),并std::make_unique<B>()返回临时对象,并且临时对象的清理在 C++ 标准中是正确指定的:它们的析构函数将被触发并释放内存。因此,如果可以的话,总是更喜欢使用std::make_unique()和分配对象std::make_shared()