直接调用(虚拟)析构函数是否有效

Is it valid to directly call a (virtual) destructor?

本文关键字:析构函数 是否 有效 虚拟 调用      更新时间:2023-10-16

在这个答案中,Ryan直接调用了虚拟析构函数。我已经在VS2010中测试了代码,它正确地调用了所有析构函数(使用日志记录语句进行了测试)。这样做真的有效吗?这种方法存在哪些问题、缺陷甚至优点?

我只能将其视为一种真正强制重置实际类型的方法,即使它们不覆盖虚拟reset函数,因为它们至少必须在析构函数中进行清理。

另外,对析构函数的调用究竟会带来什么样的副作用?在这样的析构函数调用之后使用对象是未定义的行为吗?如果立即通过new (this) MyClass();调用重新初始化它怎么办?

手动调用析构函数是一件完全有效的事情,无论它是否是虚拟的。你只需要确保它只为每个构造函数调用调用一次。

Is it undefined behaviour to use the object after such a destructor call? 

是的。

What if one immediatly reinitializes it with a new (this) MyClass(); call?

仍然可怕地不确定。

不要手动销毁对象,除非您必须手动放置它,例如使用放置 new 或等效放置,并且绝对不要像这样重新初始化被破坏的对象并希望避免 UB。像std::vector这样的类非常明确地使访问被销毁的对象成为UB,即使你随后在其位置创建一个新元素,它仍然是UB。

涉及一种且仅一种构造的有效使用示例:

typedef boost::aligned_storage<
    sizeof(T), boost::alignement_of<T>::value>::type arena_type;
arena_type arena;
T* p = new (&arena) T();
p->~T();
// Don't touch p now

这在实现变体类型时很有用(警告:异常安全留给读者练习)。C++0x 无限制联合对类类型也有类似的用法。

请注意,对于类类型,如果未调用析构函数,则上述内容将是 UB。

只要您在预先分配的 POD 内存块之上调用放置新,那么释放调用任何析构函数(无论是否虚拟)都是完全有效的。

放置新的和显式的释放分配器调用将只调用引用区域中的构造函数和析构函数,因此内存分配有效地被排除在对象生命周期之外