【问题标题】:Differences between std::make_unique and std::unique_ptr with newstd::make_unique 和 std::unique_ptr 与 new 的区别
【发布时间】:2014-04-29 13:18:58
【问题描述】:

std::make_unique 是否有像 std::make_shared 这样的效率优势?

相比手动构造std::unique_ptr

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

【问题讨论】:

  • make_shared 是否比仅编写长手写代码更有效率?
  • @EdHeal 可以,因为make_shared 可以在一次分配中同时为对象和控制块分配空间。这样做的代价是对象不能与控制块分开释放,所以如果你经常使用weak_ptr,那么你最终可能会使用更多的内存。
  • 也许这是一个很好的起点stackoverflow.com/questions/9302296/…
  • 详细解释见此链接:herbsutter.com/gotw/_102

标签: c++ c++11 c++14 smart-pointers unique-ptr


【解决方案1】:

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*
    
  • 添加make_unique 最终意味着我们可以告诉人们“从不”使用new,而不是之前的规则“从不”使用new,除非你创建unique_ptr

还有第三个原因:

  • make_unique 不需要冗余类型使用。 unique_ptr&lt;T&gt;(new T()) -> make_unique&lt;T&gt;()

没有一个原因涉及像使用 make_shared 那样提高运行时效率(由于避免了第二次分配,代价是可能会出现更高的峰值内存使用率)。

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

【讨论】:

  • std::unique_ptrstd::shared_ptr 更有意义,这就是我们可以告诉人们“永远不要使用new”的原因。
  • @TimothyShields 是的,这就是我的意思。只是在 C++11 中我们有 make_shared,所以 make_unique 是之前缺失的最后一块。
  • 您能以任何方式简要提及或链接到不使用未命名临时人员的原因吗?
  • 其实,从stackoverflow.com/a/19472607/368896,我知道了...从那个答案,考虑以下函数调用ff(unique_ptr&lt;T&gt;(new T), function_that_can_throw()); - 引用答案:编译器允许调用(按顺序):new Tfunction_that_can_throw()unique_ptr&lt;T&gt;(...)。显然,如果function_that_can_throw 真的抛出了,那么你就会泄漏。 make_unique 阻止了这种情况。 所以,我的问题得到了回答。
  • 我曾经不得不使用 std::unique_ptr(new T()) 的一个原因是因为 T 的构造函数是私有的。即使对 std::make_unique 的调用在类 T 的公共工厂方法中,它也没有编译,因为 std::make_unique 的底层方法之一无法访问私有构造函数。我不想让那个方法成为朋友,因为我不想依赖 std::make_unique 的实现。所以唯一的解决方案是,在我的 T 类工厂方法中调用 new,然后将其包装在 std::unique_ptr.
【解决方案2】:

std::make_uniquestd::make_shared 有两个原因:

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

这与运行时效率无关。有一点关于控制块和T 被同时分配,但我认为这更多的是奖励,而不是这些函数存在的动机。

【讨论】:

  • 它们也是为了异常安全而存在的。
  • @0x499602D2 那,很好的补充。 This page 谈到这一点。
  • 对于未来的读者,C++17 不允许函数参数的交错,因此异常安全的参数不再成立。两个并行std::make_shared 的内存分配将确保其中至少一个在另一个内存分配发生之前被包裹在智能指针中,因此不会泄漏。
【解决方案3】:

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

【讨论】:

  • 是否有计划修复 C++,使 make_unique 和 make_shared 可以访问私有构造函数?
  • @nyanpasu64 make_unique 不需要修复。您始终可以使用私有构造函数使其成为您班级的朋友。
【解决方案4】:

考虑函数调用

void function(std::unique_ptr<A>(new A()), std::unique_ptr<B>(new B())) { ... }

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

void function(std::make_unique<A>(), std::make_unique<B>()) { ... }

这里的重点是std::make_unique&lt;A&gt;std::make_unique&lt;B&gt; 现在是临时对象,C++ 标准中正确指定了临时对象的清理:它们的析构函数将被触发并释放内存。因此,如果可以,请始终使用std::make_uniquestd::make_shared 分配对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-04
    • 2013-08-24
    • 2014-10-29
    • 2012-12-01
    • 1970-01-01
    相关资源
    最近更新 更多