使用 std::make_unique 相对于 new 运算符的优势

Advantages of using std::make_unique over new operator

使用 std::make_unique 相对于 new 运算符初始化 std::unique_ptr 有什么优势?

换句话说,为什么

std::unique_ptr<SomeObject> a = std::make_unique(SomeObject(...))

比做更好

std::unique_ptr<SomeObject> a = new SomeObject(...)

我试着在网上查了很多,我确实知道在现代 C++ 中避免运算符 new 是一个很好的经验法则,但我不确定在这种情况下有什么优势.它是否可以防止可能发生的任何类型的内存泄漏?使用 std::make_unique 是否比使用 new 更快?

优势

  • make_unique 教导用户“永远不要说 new/deletenew[]/delete[]" 无免责声明。

  • make_uniquemake_shared有两个优点(不包括第三个优点,提高效率)。首先,unique_ptr<LongTypeName> up(new LongTypeName(args))必须提到LongTypeName两次,而auto up = make_unique<LongTypeName>(args)必须提到一次。

  • make_unique 防止未指定的评估顺序 由 foo(unique_ptr<X>(new X), unique_ptr<Y>(new Y)) 等表达式触发的泄漏。 (遵循“永远不要说 new”的建议比 “永远不要说 new,除非你立即把它交给一个有名字的 unique_ptr”。)

  • make_unique 是为异常安全而精心实现的,建议直接调用 unique_ptr 构造函数。

什么时候不使用make_unique

  • 如果您需要自定义删除器或正在采用来自其他地方的原始指针,请不要使用 make_unique

来源

  1. Proposal of std::make_unique.
  2. Herb Sutter's GotW #89 Solution: Smart Pointers

不同的是std::make_unique returns an object of type std::unique_ptrnew returns指向创建对象的指针。对于内存分配失败,它们都会抛出。 等等,没那么简单。进一步阅读。

考虑下面这样一个函数:

void func(ClassA* a, ClassB* b){
     ......
}

当你拨打类似func(new A(), new B())的电话时;编译器可以选择从左到右或以任何它希望的顺序计算函数参数。让我们假设从左到右的评估:当第一个 new 表达式成功但第二个 new 表达式抛出时会发生什么?

这里真正的危险是当你捕捉到这样的异常时;是的,你可能已经捕获了new B()抛出的异常,并恢复正常执行,但是new A()已经成功了,它的内存会悄无声息地泄漏。没人清理它... * sobs...

但是对于 make_unique,您不会有泄漏,因为堆栈展开将会发生(并且先前创建的对象的析构函数将 运行)。因此,对 make_unique 的偏好将限制您对 exception safety. In this case, std::make_unique provides a "Basic Exception Safety" 的偏好,即 new 分配的内存和创建的对象无论如何都不会被孤立。甚至直到时间的尽头...:-)

你应该阅读 Herb Sutter GoTW102