std :: make_unique和std :: unique_ptr与new之间的区别


130

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

与手动构建相比std::unique_ptr

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

是否make_shared有任何效率上只是写了长手代码?
Ed Heal 2014年

9
@EdHeal因为make_shared可以在一个分配中一起分配对象的空间和控制块的空间,所以它可以。这样做的代价是无法将对象与控制块分开释放,因此,如果使用过多的对象,weak_ptr最终可能会使用更多的内存。
bames53 2014年

也许这是一个很好的起点stackoverflow.com/questions/9302296/...
埃德愈合

Answers:


140

背后的动机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_uniquefinal”意味着我们可以告诉人们“从不使用”,new而不是以前的规则“ new除非您做出unique_ptr”,否则要“从不使用”。

还有第三个原因:

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

这些原因均不涉及提高使用方式的运行时效率make_shared(由于避免了第二次分配,以可能更高的峰值内存使用为代价)。

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


要说得更合理std::unique_ptrstd::shared_ptr这就是为什么我们可以告诉人们“从不使用” new
蒂莫西·希尔兹

2
@TimothyShields是的,这就是我的意思。只是在C ++ 11中我们拥有make_sharedmake_unique最后缺少的也是最后一块。
bames53 2014年

1
您能以任何方式简要提及或链接不使用未命名临时人员的原因吗?
Dan Nissenbaum'3

14
其实,从stackoverflow.com/a/19472607/368896,我知道了......从这个问题的答案,考虑下面的函数调用ff(unique_ptr<T>(new T), function_that_can_throw());-引用了答案:编译器允许调用(按顺序): ,,new T 。显然,如果实际抛出,则可能会泄漏。防止这种情况。function_that_can_throw()unique_ptr<T>(...)function_that_can_throwmake_unique 因此,我的问题得到了回答。
Dan Nissenbaum'3

3
我曾经不得不使用std :: unique_ptr <T>(new T())的原因之一是因为T的构造函数是私有的。即使对std :: make_unique的调用是在类T的公共工厂方法中,它也无法编译,因为std :: make_unique的基础方法之一无法访问私有构造函数。我不想让该方法成为朋友,因为我不想依赖于std :: make_unique的实现。因此,唯一的解决方案是,在我的类T的工厂方法中调用new,然后将其包装在std :: unique_ptr <T>中。
Patrick

14

std::make_unique并且std::make_shared有两个原因:

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

这并不是真正意义上的运行时效率。关于控制块和同时T分配全部内容有些不足,但是我认为这更多的是花红,而不是这些功能存在的动力。


他们也在那里是为了例外安全。
0x499602D2 2014年

@ 0x499602D2而且,很好。本页讨论了这一点。
蒂莫西·希尔兹

对于将来的读者,C ++ 17不允许函数参数的交织,因此异常安全性的参数不再适用。两个并行的内存分配std::make_shared将确保它们中的至少一个在另一个内存分配发生之前被包装在智能指针中,因此不会泄漏。
MathBunny


0

考虑函数调用

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<A>并且std::make_unique<B>现在是临时对象,并且在C ++标准中正确指定了临时对象的清除:将触发它们的析构函数并释放内存。因此,如果可以的话,请始终喜欢使用std::make_unique和分配对象std::make_shared

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.