[英]Why use std::make_unique in C++17?
据我了解,C++14 引入了std::make_unique
,因为由于未指定参数评估顺序,这是不安全的:
f(std::unique_ptr<MyClass>(new MyClass(param)), g()); // Syntax A
(解释:如果评估先为原始指针分配内存,然后调用g()
并在std::unique_ptr
构造之前抛出异常,则内存泄漏。)
调用std::make_unique
是一种限制调用顺序的方法,从而使事情变得安全:
f(std::make_unique<MyClass>(param), g()); // Syntax B
从那时起,C++17 明确了求值顺序,使 Syntax A 也变得安全,所以这是我的问题:在 C++17 中,是否还有理由使用std::make_unique
而不是std::unique_ptr
的构造函数? 你能举一些例子吗?
截至目前,我能想到的唯一原因是它只允许键入MyClass
一次(假设您不需要依赖std::unique_ptr<Base>(new Derived(param))
的多态性)。 但是,这似乎是一个非常薄弱的理由,尤其是当std::make_unique
不允许指定删除器而std::unique_ptr
的构造函数允许指定删除器时。
需要明确的是,我并不是在提倡从标准库中删除std::make_unique
(保持它至少对于向后兼容是有意义的),而是想知道是否仍然存在强烈偏好它的情况std::unique_ptr
你是对的,主要原因已被删除。 仍然有不使用新的准则,并且它是较少输入的原因(不必重复输入或使用单词new
)。 诚然,这些都不是强有力的论据,但我真的不喜欢在我的代码中看到new
的东西。
也不要忘记一致性。 你绝对应该使用make_shared
所以使用make_unique
是自然的并且符合模式。 然后将std::make_unique<MyClass>(param)
更改为std::make_shared<MyClass>(param)
(或相反)变得微不足道,其中语法 A 需要更多的重写。
make_unique
将T
与T[]
和T[N]
区分开来, unique_ptr(new...)
则不然。
通过将经过new[]
编辑的指针传递给unique_ptr<T>
,或者将经过new
编辑的指针传递给unique_ptr<T[]>
,您可以轻松获得未定义的行为。
原因是代码更短,没有重复。 比较
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
您保存MyClass
、 new
和大括号。 与ptr相比, make只多花一个字符。
每次使用new
都必须格外仔细地审核,以确保生命周期的正确性; 它会被删除吗? 只有一次?
每次使用make_unique
都不是为了那些额外的特征; 只要拥有对象具有“正确”的生命周期,它就会递归地使唯一指针具有“正确”。
现在,确实unique_ptr<Foo>(new Foo())
在所有方面都相同1到make_unique<Foo>()
; 它只需要一个更简单的“grep 您的源代码,以便对new
的所有用途进行审核”。
1实际上是一般情况下的谎言。 完美转发并不完美, {}
、默认初始化、数组都是例外。
从那时起,C++17 明确了求值顺序,使 Syntax A 也变得安全
那真的不够好。 依靠最近引入的技术条款作为安全保证并不是一个非常稳健的做法:
new
,例如通过复制粘贴您的示例。new
东西移到别处(好吧,当然,这种可能性不大)。通常,最好让您的代码适当/健壮/明确有效,而无需诉诸语言分层,在标准中查找次要或晦涩的技术条款。
(这基本上与我在这里提出的关于元组销毁顺序的论点相同。)
考虑 void function(std::unique_ptr(new A()), std::unique_ptr(new B())) {... }
假设 new A() 成功,但 new B() 抛出异常:您捕获它以恢复程序的正常执行。 不幸的是,C++ 标准不要求对象 A 被销毁并释放其内存:内存悄无声息地泄漏并且没有办法清理它。 通过将 A 和 B 包装到 std::make_uniques 中,您可以确定不会发生泄漏:
void function(std::make_unique(), std::make_unique()) {... } 这里的重点是 std::make_unique 和 std::make_unique 现在是临时对象,并且在中正确指定了临时对象的清理C++ 标准:它们的析构函数将被触发并释放内存。 所以如果可以的话,总是喜欢使用 std::make_unique 和 std::make_shared 分配对象。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.