为什么在 C++17 中使用 std::make_unique?
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 明确了评估顺序,使语法 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
原因是代码更短,没有重复。比较
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
您省去了 MyClass
、new
和大括号。与 ptr.
相比,make 只多花一个字符
您说得对,删除了主要原因。仍然有 不要使用新的 准则,并且它是较少键入的原因(不必重复键入或使用单词 new
)。不可否认,这些论据并不充分,但我真的很喜欢在我的代码中看不到 new
。
另外不要忘记一致性。你绝对应该使用 make_shared
所以使用 make_unique
是自然的并且符合模式。然后将 std::make_unique<MyClass>(param)
更改为 std::make_shared<MyClass>(param)
(或相反)是微不足道的,其中语法 A 需要更多的重写。
每次使用 new
都必须格外仔细地审核其生命周期的正确性;它会被删除吗?只有一次?
每次使用 make_unique
都不是为了那些额外的特征;只要拥有对象的生命周期为 "correct",它就会递归地使唯一指针具有 "correct".
现在,unique_ptr<Foo>(new Foo())
确实在所有方面都是相同的1 到 make_unique<Foo>()
;它只需要一个更简单的 "grep your source code for all uses of new
to audit them".
1一般情况下其实是骗人的。完美转发不完美,{}
,默认init,数组都是例外
make_unique
区分 T
与 T[]
和 T[N]
,unique_ptr(new ...)
则不然。
您可以通过将 new[]
ed 的指针传递给 unique_ptr<T>
或将 new
ed 的指针传递给 [=18= 来轻松获得未定义的行为].
Since then, C++17 has clarified the evaluation order, making Syntax A safe too
这真的不够好。依靠最近引入的技术条款作为安全保证并不是一个非常稳健的做法:
- 有人可能会在 C++14 中编译此代码。
- 您会鼓励在其他地方使用原始
new
,例如通过复制粘贴您的示例。
- 如S.M。建议,由于存在代码重复,一种类型可能会在没有更改另一种类型的情况下进行更改。
- 某种自动 IDE 重构可能会将
new
移动到其他地方(好的,当然,这种可能性不大)。
一般来说,最好让您的代码appropriate/robust/clearly有效而无需诉诸语言分层,查找标准中次要或模糊的技术条款。
(这与我 关于元组销毁顺序的论点基本相同。)
考虑一下
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.
分配对象
据我了解,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
原因是代码更短,没有重复。比较
f(std::unique_ptr<MyClass>(new MyClass(param)), g());
f(std::make_unique<MyClass>(param), g());
您省去了 MyClass
、new
和大括号。与 ptr.
您说得对,删除了主要原因。仍然有 不要使用新的 准则,并且它是较少键入的原因(不必重复键入或使用单词 new
)。不可否认,这些论据并不充分,但我真的很喜欢在我的代码中看不到 new
。
另外不要忘记一致性。你绝对应该使用 make_shared
所以使用 make_unique
是自然的并且符合模式。然后将 std::make_unique<MyClass>(param)
更改为 std::make_shared<MyClass>(param)
(或相反)是微不足道的,其中语法 A 需要更多的重写。
每次使用 new
都必须格外仔细地审核其生命周期的正确性;它会被删除吗?只有一次?
每次使用 make_unique
都不是为了那些额外的特征;只要拥有对象的生命周期为 "correct",它就会递归地使唯一指针具有 "correct".
现在,unique_ptr<Foo>(new Foo())
确实在所有方面都是相同的1 到 make_unique<Foo>()
;它只需要一个更简单的 "grep your source code for all uses of new
to audit them".
1一般情况下其实是骗人的。完美转发不完美,{}
,默认init,数组都是例外
make_unique
区分 T
与 T[]
和 T[N]
,unique_ptr(new ...)
则不然。
您可以通过将 new[]
ed 的指针传递给 unique_ptr<T>
或将 new
ed 的指针传递给 [=18= 来轻松获得未定义的行为].
Since then, C++17 has clarified the evaluation order, making Syntax A safe too
这真的不够好。依靠最近引入的技术条款作为安全保证并不是一个非常稳健的做法:
- 有人可能会在 C++14 中编译此代码。
- 您会鼓励在其他地方使用原始
new
,例如通过复制粘贴您的示例。 - 如S.M。建议,由于存在代码重复,一种类型可能会在没有更改另一种类型的情况下进行更改。
- 某种自动 IDE 重构可能会将
new
移动到其他地方(好的,当然,这种可能性不大)。
一般来说,最好让您的代码appropriate/robust/clearly有效而无需诉诸语言分层,查找标准中次要或模糊的技术条款。
(这与我
考虑一下 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.
分配对象