据我所知,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阐明了评估顺序,也使语法A变得安全,因此这是我的问题:在std::make_unique
的上方,是否还有理由使用std::unique_ptr
C ++ 17中的构造函数?你能举一些例子吗?
到目前为止,我能想象的唯一原因是它只允许键入MyClass
(假设您不需要依靠std::unique_ptr<Base>(new Derived(param))
的多态性)。但是,这似乎是一个很弱的原因,尤其是当std::make_unique
的构造函数允许std::unique_ptr
不允许指定删除程序时。
为了清楚起见,我不主张从标准库中删除std::make_unique
(至少保持向后兼容才有意义),而是想知道是否仍然存在这种情况是std::unique_ptr
答案 0 :(得分:63)
您说对了,主要原因已删除。仍然有不要使用新的准则,并且键入的原因更少(不必重复键入或使用单词new
)。诚然,这些并不是强力的论据,但我真的很喜欢在代码中看不到new
。
也不要忘记一致性。您绝对应该使用make_shared
,因此使用make_unique
是自然的并且适合该模式。然后,在语法A需要更多重写的情况下,将std::make_unique<MyClass>(param)
更改为std::make_shared<MyClass>(param)
(或相反)很简单。
答案 1 :(得分:42)
make_unique
将T
与T[]
和T[N]
区别开来,unique_ptr(new ...)
则没有。
通过将new[]
的指针传递到unique_ptr<T>
,或通过将new
的指针传递到unique_ptr<T[]>
,可以轻松地获得未定义的行为。
答案 2 :(得分:20)
原因是代码较短,没有重复。比较
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
您保存MyClass
,new
和花括号。与 ptr 相比,在 make 中仅花费一个字符。
答案 3 :(得分:16)
每次使用new
时都必须经过仔细审核,以确保生命周期的正确性;它会被删除吗?只有一次?
每次使用make_unique
都不意味着那些额外的特征;只要拥有对象的生存期为“正确”,它就会递归地使唯一指针具有“正确”。
现在,unique_ptr<Foo>(new Foo())
确实在所有方面都与make_unique<Foo>()
相同 1 ;它只需要一个简单的“为new
的所有使用复制您的源代码来审核它们”。
1 实际上是一个谎言。完美的转发并不完美,{}
,默认初始化,数组都是例外。
答案 4 :(得分:0)
从那时起,C ++ 17明确了评估顺序,从而使语法A也很安全
那真的不够好。依靠最近引入的技术条款来保证安全并不是一个非常可靠的做法:
new
,例如通过复制粘贴您的示例。new
移到其他地方(好的,当然,机会不大)。通常,您的代码应该适当/健壮/明确有效地 ,而无需借助语言排版,查找标准中的次要或晦涩的技术条款,是个好主意。
(这与我为元组破坏的顺序here所做的论点基本相同。)
答案 5 :(得分:-1)
考虑 无效函数(std :: unique_ptr(new A()),std :: unique_ptr(new B())){...}
假设新的A()成功,但是新的B()抛出异常:您捕获到该异常以恢复程序的正常执行。不幸的是,C ++标准不要求销毁对象A并释放其内存:内存静默泄漏,无法清除它。通过将A和B包装到std :: make_uniques中,可以确保不会发生泄漏:
无效函数(std :: make_unique(),std :: make_unique()){...} 这里的要点是,std :: make_unique和std :: make_unique现在是临时对象,并且在C ++标准中正确指定了临时对象的清除:将触发它们的析构函数并释放内存。因此,如果可以的话,总是喜欢使用std :: make_unique和std :: make_shared分配对象。