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

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

本文关键字:std unique 之间 ptr make new      更新时间:2023-10-16

std::make_uniquestd::make_shared一样具有效率优势吗?

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

std::make_unique<int>(1);         // vs
std::unique_ptr<int>(new int(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,而不是之前的规则"除非你制作unique_ptr,否则永远不要使用new"。

还有第三个原因:

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

这些原因都不涉及像使用make_shared那样提高运行时效率(因为避免了第二次分配,代价是潜在的更高峰值内存使用率(。

*预计C++17将包含一个规则更改,这意味着它不再是不安全的。参见C++委员会文件P0400R0和P0145R3。

std::make_uniquestd::make_shared的存在有两个原因:
  1. 这样就不必显式地列出模板类型的参数
  2. 使用std::unique_ptrstd::shared_ptr构造函数的额外异常安全性。(请参阅此处的"注释"部分。(

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

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

考虑函数调用

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

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

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

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