是否可以重新分配 std::unique_ptr,使其旧值在构造新值之前*被销毁?
Can a std::unique_ptr be reassigned such that its old value is destroyed *before* the new one is constructed?
我有兴趣将旧的个人项目更新为现代 C++。我很欣赏 RAII 如何简化清理:不是创建一个 new
对象并记住在函数中的每个 return 点之前 delete
它,只是 make_unique
它将被适当地销毁.但是在比较生成的程序集时我有点挑剔。
假设有一个 class 方法用新值替换其中一个 unique_ptr
成员:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo = std::make_unique<int>(x * 3 + 5);
}
这将创建一个 new
int
,将其分配给 m_foo
,然后 delete
m_foo
的旧值。但这与旧行为不太一样,旧行为可以 delete
旧值,然后创建一个 new
并将其分配给 m_foo
:
// int *MyClass::m_foo;
void MyClass::refresh_foo(int x) {
delete m_foo;
m_foo = new int(x * 3 + 5);
}
从 Compiler Explorer 开始,gcc、clang 和 MSVC 都为旧方法生成的代码少于新方法。我理解为什么会这样,因为这是所有表达式的求值顺序; p = q;
必须先构造 q
。当然 m_foo
在 delete
和 new
行之间有一个无效值。但是有没有办法让 unique_ptr
破坏它的旧值,然后在一个表达式中创建一个新的值?一个“replace_unique
”?似乎在处理大对象时会有所帮助,因此它们中的两个不会不必要地共存。
编辑: 明确地说,我在这里只是使用 int
作为一个简单的例子。我的实际用例是 class 个我自己的或来自图书馆的。并且 m_foo.reset(new int(x * 3 + 5));
与旧的做事方式相比自然有同样的问题,因为它也必须在分配之前构造 new
int
并且 delet
ing 前一个.
您可以在需要时使用unique_ptr::reset()
确定性地销毁当前保存的对象。用 nullptr
指针更新 unique_ptr
,然后用新的对象指针更新它,例如:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo.reset(); // <- int is destroyed here
m_foo = std::make_unique<int>(x * 3 + 5);
}
我接受了在 m_foo = std::make_unique<int>(...)
之前调用 m_foo.reset()
的答案,因为这是解决所述问题的最简单方法:在构造新值之前先销毁原始值。但是,这确实会导致额外的 delete
调用,这不是必需的,并且不如使用原始指针的“前现代”方式最佳。
这种方法,至少在Compiler Explorer和-Ofast
中,避免了第二次delete
调用,并且与旧方法相比只多了一个mov
指令(在分配新构造的值之前显式设置 m_foo
为 nullptr
):
// std::unique_ptr<int> MyClass::m_foo;
void refresh_smart(int x) {
m_foo = nullptr;
auto new_foo = std::make_unique<int>(x * 3 + 5);
m_foo.swap(new_foo);
new_foo.release(); // no memory leak since it's null
}
作为可重复使用的模板函数:
template<typename T, typename... Ps>
void remake_unique(std::unique_ptr<T> &p, Ps... args) {
p = nullptr;
auto new_p = std::make_unique<T>(args...);
p.swap(new_p);
new_p.release();
}
// std::unique_ptr<int> MyClass::m_foo;
void refresh_smart(int x) {
remake_unique(m_foo, x * 3 + 5);
}
我有兴趣将旧的个人项目更新为现代 C++。我很欣赏 RAII 如何简化清理:不是创建一个 new
对象并记住在函数中的每个 return 点之前 delete
它,只是 make_unique
它将被适当地销毁.但是在比较生成的程序集时我有点挑剔。
假设有一个 class 方法用新值替换其中一个 unique_ptr
成员:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo = std::make_unique<int>(x * 3 + 5);
}
这将创建一个 new
int
,将其分配给 m_foo
,然后 delete
m_foo
的旧值。但这与旧行为不太一样,旧行为可以 delete
旧值,然后创建一个 new
并将其分配给 m_foo
:
// int *MyClass::m_foo;
void MyClass::refresh_foo(int x) {
delete m_foo;
m_foo = new int(x * 3 + 5);
}
从 Compiler Explorer 开始,gcc、clang 和 MSVC 都为旧方法生成的代码少于新方法。我理解为什么会这样,因为这是所有表达式的求值顺序; p = q;
必须先构造 q
。当然 m_foo
在 delete
和 new
行之间有一个无效值。但是有没有办法让 unique_ptr
破坏它的旧值,然后在一个表达式中创建一个新的值?一个“replace_unique
”?似乎在处理大对象时会有所帮助,因此它们中的两个不会不必要地共存。
编辑: 明确地说,我在这里只是使用 int
作为一个简单的例子。我的实际用例是 class 个我自己的或来自图书馆的。并且 m_foo.reset(new int(x * 3 + 5));
与旧的做事方式相比自然有同样的问题,因为它也必须在分配之前构造 new
int
并且 delet
ing 前一个.
您可以在需要时使用unique_ptr::reset()
确定性地销毁当前保存的对象。用 nullptr
指针更新 unique_ptr
,然后用新的对象指针更新它,例如:
// std::unique_ptr<int> MyClass::m_foo;
void MyClass::refresh_foo(int x) {
m_foo.reset(); // <- int is destroyed here
m_foo = std::make_unique<int>(x * 3 + 5);
}
我接受了在 m_foo = std::make_unique<int>(...)
之前调用 m_foo.reset()
的答案,因为这是解决所述问题的最简单方法:在构造新值之前先销毁原始值。但是,这确实会导致额外的 delete
调用,这不是必需的,并且不如使用原始指针的“前现代”方式最佳。
这种方法,至少在Compiler Explorer和-Ofast
中,避免了第二次delete
调用,并且与旧方法相比只多了一个mov
指令(在分配新构造的值之前显式设置 m_foo
为 nullptr
):
// std::unique_ptr<int> MyClass::m_foo;
void refresh_smart(int x) {
m_foo = nullptr;
auto new_foo = std::make_unique<int>(x * 3 + 5);
m_foo.swap(new_foo);
new_foo.release(); // no memory leak since it's null
}
作为可重复使用的模板函数:
template<typename T, typename... Ps>
void remake_unique(std::unique_ptr<T> &p, Ps... args) {
p = nullptr;
auto new_p = std::make_unique<T>(args...);
p.swap(new_p);
new_p.release();
}
// std::unique_ptr<int> MyClass::m_foo;
void refresh_smart(int x) {
remake_unique(m_foo, x * 3 + 5);
}