正在从vtable的析构函数安全抛出异常
Is throwing an exception from destructor safe for the vtable?
请考虑以下示例:
#include <csignal>
class A
{
public:
virtual ~A() {}
virtual void foo() = 0;
};
class B : public A
{
public:
virtual ~B() { throw 5; }
virtual void foo() {}
};
int main(int, char * [])
{
A * b = new B();
try
{
delete b;
}
catch ( ... )
{
raise(SIGTRAP);
}
return 0;
}
我一直认为(天真的我),当程序在这种情况下进入catch
部分时,b
指向的对象B
将完好无损,因为异常将"取消"(如果安全编程)析构函数的影响是非常合乎逻辑的。但是,当我尝试在gdb中运行这个片段并到达catch
部分的断点时,我看到B对象不见了,只剩下A基对象,因为vtable看起来像这样:
(gdb) i vtbl b
vtable for 'A' @ 0x400cf0 (subobject @ 0x603010):
[0]: 0x0
[1]: 0x0
[2]: 0x4008e0 <__cxa_pure_virtual@plt>
我的问题是:如果我非常想从析构函数抛出异常,有没有办法避免vtable的(半)破坏?
我一直认为(天真的我),当程序在这种情况下进入catch部分时,B点处的对象B将完好无损,因为异常将具有";取消";(如果编程安全的话)析构函数的效果。
这不是真的。标准上写着:
初始化或销毁因异常而终止的任何存储持续时间的对象将为其所有完全构造的子对象执行析构函数(不包括类并集),也就是说,对于主构造函数(12.6.2)已经完成执行的子对象并且析构函数尚未开始执行。
(在N4140中为15.2/2)
也许更重要的是:
T类型对象的寿命在以下情况下结束:
--如果T是具有非平凡析构函数(12.4)的类类型,则析构函数调用启动
(N4140中为3.8/1.3)
由于b
的每一个成员和基都是完全构造的,并且它们的析构函数都不在其中,但它们将被视为已被破坏因此,在catch
块中,b
指向的整个对象已经死了
这背后的理性可能是令人生畏的"半破坏的";对象,因为不清楚不能销毁的对象的状态应该是什么。例如,如果只有一些成员已经销毁了呢?
甚至标准本身也建议不要让异常离开析构函数。正如我之前在评论中所写的,抛出析构函数是很奇怪的。
我们可以从上面的引用中得到一个很好的规则:当对象的构造函数完成而不抛出时,对象就开始存在,而当其析构函数开始执行时,它就停止存在,无论它如何退出(这在标准的其他地方得到了更明确的重申。这也有例外,不要在意。)
所以总结一下:
如果我非常想从析构函数抛出异常,有没有办法避免vtable的(半)破坏?
没有。一旦你进入析构函数,你的对象就完成了。
当程序进入case,进入catch部分,然后B点所在的对象B完整,因为异常将具有"取消"(如果安全编程)析构函数的效果。
没有。当对象的析构函数启动时,对象的生存期结束。
不能取消析构函数。
正如其他人所说,在C++中抛出析构函数是很奇怪的,除了特殊情况外,你需要避免它们。
就实例而言,从析构函数抛出是定义良好且安全的。您开始遇到问题的地方是数组(因为它无法完成删除数组的操作,而且您无法将其取回)和catch子句(最终可能调用terminate)。如果析构函数抛出,编写异常安全代码也很困难(我认为这实际上是不可能的,但还没有准备好从内存中声明这一点)。
不过,我用过抛出析构函数来做一些事情。例如,我使用的API可能会返回错误代码并分配错误blob。我写了一个小范围保护的东西,它会分发引用来放入数据,并检查析构函数中的错误条件。如果它看到一个异常,就会将其转换为异常并抛出
这样的结构在技术上是安全的,但你有点想避免它,直到你知道自己在做什么。您必须明确这些东西不能存储在向量或数组中,并且可能会使异常安全代码变得不安全。主要的问题是,几乎每个人都希望所有的析构函数都是另一个。
- 什么时候调用组成单元对象的析构函数
- 如果C++类在类方法中具有动态分配,但没有构造函数/析构函数或任何非静态成员,那么它仍然是POD类型吗
- 内联映射初始化的动态atexit析构函数崩溃
- 什么时候调用析构函数
- 优先顺序:智能指针和类析构函数
- C++-明确何时以及如何调用析构函数
- 析构函数和线程安全
- 在特殊情况下使析构函数不是虚拟的,并删除基指针是否安全
- 用return语句结束析构函数安全吗
- 调用remove()删除析构函数中的文件是否安全
- Qt:写这个类的析构函数的正确和安全的方法是什么
- 根据C++标准,显式调用构造函数和析构函数是否安全
- 在抽象构造函数/析构函数中调用纯虚拟函数安全吗
- 多个线程调用exit()时的安全静态析构函数
- 正在从vtable的析构函数安全抛出异常
- c++析构函数调用数组索引?在非线程安全的refcount对象上崩溃
- c++删除/析构函数安全调用
- 在派生类中声明非虚析构函数安全吗?
- 在带有虚析构函数的基类的子类中不指定析构函数是否安全?
- 什么时候在构造函数和析构函数中调用 this-> 是安全的