使用 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
/delete
和
new[]
/delete[]
" 无免责声明。
make_unique
和make_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
。
来源
不同的是std::make_unique
returns an object of type std::unique_ptr
和new
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
使用 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
/delete
和new[]
/delete[]
" 无免责声明。make_unique
和make_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
。
来源
不同的是std::make_unique
returns an object of type std::unique_ptr
和new
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