像普通方法一样调用构造函数和析构函数的注意事项和风险?
Caveats and risks of calling a constructor and destructor like common methods?
我的程序中有一点需要重置某个对象的状态"to factory defaults"。该任务归结为执行析构函数和构造函数中编写的所有内容。我可以删除并重新创建该对象——但我可以将析构函数和构造函数作为普通对象调用吗? (特别是,我不想将更新后的指针重新分配给新实例,因为它在程序其他地方的副本中徘徊)。
MyClass {
public:
MyClass();
~MyClass();
...
}
void reinit(MyClass* instance)
{
instance->~MyClass();
instance->MyClass();
}
我可以这样做吗?如果是这样,是否有任何风险、注意事项或我需要记住的事情?
Can I do this? If so, are there any risks, caveats, things I need to remember?
不,你不能这样做。除了析构函数调用在技术上是可行的,它只是未定义的行为。
假设您已经实现了 class correctly 的赋值运算符,您可以只写:
void reinit(MyClass* instance) {
*instance = MyClass();
}
如果您的赋值运算符和构造函数编写正确,您应该能够将其实现为:
void reinit(MyClass* instance)
{
*instance = MyClass();
}
如果您的赋值运算符和构造函数编写不正确,请修复它们。
将重新初始化实现为先销毁再构造的警告是,如果构造函数失败并抛出异常,对象将被销毁两次,而不会在第一次和第二次销毁之间再次构造(一次由您的手册销毁,一次是在其所有者超出范围时发生的自动销毁)。这有未定义的行为。
您应该使用智能指针并依靠移动语义来获得您想要的行为。
auto classObj = std::make_unique<MyClass>();
这会创建一个包装指针来为您处理动态内存。假设您已准备好将 classObj
重置为出厂默认设置,您只需要:
classObj = std::make_unique<MyClass>();
这个"move-assignment"操作会调用MyClass
的析构函数,然后重新分配classObj
智能指针指向一个新构造的MyClass
实例。起泡沫,冲洗,必要时重复。换句话说,您不需要 reinit
函数。然后当classObj
被销毁时,它的内存就被清理了。
您可以尝试使用 placement new 表达式
new (&instance) MyClass()
instance->MyClass();
不合法,一定会编译出错。
instance->~MyClass();
是可以的。这会做以下两件事之一:
- 没有,如果
MyClass
有一个平凡的析构函数
- 运行析构函数中的代码并结束对象的生命周期,否则。
如果在对象的生命周期结束后使用它,则会导致未定义的行为。
很少写 instance->~MyClass();
,除非您首先使用 placement new 创建了对象,或者您将重新创建对象展示位置新。
如果您不知道,placement new 会在您已经分配存储空间时创建一个对象。例如这是合法的:
{
std::string s("hello");
s.~basic_string();
new(&s) std::string("goodbye");
std::cout << s << '\n';
}
你可以使用 placement-new:
void reinit(MyClass* instance)
{
instance->~MyClass();
new(instance) MyClass();
}
所有指针仍然有效。
或者作为成员函数:
void MyClass::reinit()
{
~MyClass();
new(this) MyClass();
}
这应该小心使用,参见 http://www.gotw.ca/gotw/023.htm,这是关于用这个技巧实现赋值运算符的,但这里也有一些要点:
- 构造函数不应该抛出
MyClass
不应用作基础 class
- 它会干扰 RAII,(但这可能是想要的)
.
我的程序中有一点需要重置某个对象的状态"to factory defaults"。该任务归结为执行析构函数和构造函数中编写的所有内容。我可以删除并重新创建该对象——但我可以将析构函数和构造函数作为普通对象调用吗? (特别是,我不想将更新后的指针重新分配给新实例,因为它在程序其他地方的副本中徘徊)。
MyClass {
public:
MyClass();
~MyClass();
...
}
void reinit(MyClass* instance)
{
instance->~MyClass();
instance->MyClass();
}
我可以这样做吗?如果是这样,是否有任何风险、注意事项或我需要记住的事情?
Can I do this? If so, are there any risks, caveats, things I need to remember?
不,你不能这样做。除了析构函数调用在技术上是可行的,它只是未定义的行为。
假设您已经实现了 class correctly 的赋值运算符,您可以只写:
void reinit(MyClass* instance) {
*instance = MyClass();
}
如果您的赋值运算符和构造函数编写正确,您应该能够将其实现为:
void reinit(MyClass* instance)
{
*instance = MyClass();
}
如果您的赋值运算符和构造函数编写不正确,请修复它们。
将重新初始化实现为先销毁再构造的警告是,如果构造函数失败并抛出异常,对象将被销毁两次,而不会在第一次和第二次销毁之间再次构造(一次由您的手册销毁,一次是在其所有者超出范围时发生的自动销毁)。这有未定义的行为。
您应该使用智能指针并依靠移动语义来获得您想要的行为。
auto classObj = std::make_unique<MyClass>();
这会创建一个包装指针来为您处理动态内存。假设您已准备好将 classObj
重置为出厂默认设置,您只需要:
classObj = std::make_unique<MyClass>();
这个"move-assignment"操作会调用MyClass
的析构函数,然后重新分配classObj
智能指针指向一个新构造的MyClass
实例。起泡沫,冲洗,必要时重复。换句话说,您不需要 reinit
函数。然后当classObj
被销毁时,它的内存就被清理了。
您可以尝试使用 placement new 表达式
new (&instance) MyClass()
instance->MyClass();
不合法,一定会编译出错。
instance->~MyClass();
是可以的。这会做以下两件事之一:
- 没有,如果
MyClass
有一个平凡的析构函数 - 运行析构函数中的代码并结束对象的生命周期,否则。
如果在对象的生命周期结束后使用它,则会导致未定义的行为。
很少写 instance->~MyClass();
,除非您首先使用 placement new 创建了对象,或者您将重新创建对象展示位置新。
如果您不知道,placement new 会在您已经分配存储空间时创建一个对象。例如这是合法的:
{
std::string s("hello");
s.~basic_string();
new(&s) std::string("goodbye");
std::cout << s << '\n';
}
你可以使用 placement-new:
void reinit(MyClass* instance)
{
instance->~MyClass();
new(instance) MyClass();
}
所有指针仍然有效。
或者作为成员函数:
void MyClass::reinit()
{
~MyClass();
new(this) MyClass();
}
这应该小心使用,参见 http://www.gotw.ca/gotw/023.htm,这是关于用这个技巧实现赋值运算符的,但这里也有一些要点:
- 构造函数不应该抛出
MyClass
不应用作基础 class- 它会干扰 RAII,(但这可能是想要的)