为什么析构函数不被exception调用?
我期望在这个程序中调用A::~A()
,但它不是:
#include <iostream> struct A { ~A() { std::cout << "~A()" << std::endl; } }; void f() { A a; throw "spam"; } int main() { f(); }
但是,如果我改变最后一行
int main() try { f(); } catch (...) { throw; }
那么A::~A()
被调用。
我正在编译Visual Studio 2005中的“Microsoft(R)32位C / C ++ Optimizing Compiler Version 14.00.50727.762 for 80×86”。命令行是cl /EHa my.cpp
。
编译器和往常一样吗? 标准在这个问题上说什么?
析构函数没有被调用,因为未处理的exception的terminate()在堆栈解开之前被调用。
C ++规范说明的具体细节超出了我的理解,但是使用gdb和g ++的debugging跟踪似乎certificate了这一点。
根据标准草案第15.3节第9点:
9如果程序中找不到匹配的处理程序,则函数terminate() (_except.terminate_)被调用。 堆栈是否被解开 在调用terminate()之前是实现定义的。
C ++语言规范指出: 调用在从try块到throw-expression的path上构造的自动对象的析构函数的过程称为“堆栈展开”。您的原始代码不包含try块,这就是为什么堆栈展开没有发生。
在第二个例子中,当它离开try {}模块时,dtor被调用。
在第一个例子中,当离开main()函数后程序closures时,dtor被调用,此时cout可能已经被销毁。
我也假定编译器不会生成相对于“a”的代码,因为它没有被引用,但是仍然不正确,因为析构函数需要执行某些操作。
所以,我试过在VS2008 / vc9(+ SP1),debugging和发布和〜A被抛出后,调用超出f() – 这是正确的行为,如果我是正确的。
现在我只是尝试了VS2005 / vc8(+ SP1),这是相同的行为。
我用断点来确定。 我只是检查了控制台,我也有“〜A”的消息。 也许你在其他地方做错了?
这个问题很容易谷歌,所以我在这里分享我的情况。
确保yor exeption不跨越“ extern "C"
边界或使用MSVC选项/ EHs(使用Extern C函数(/ EHs)启用C ++ exeptions = Yes)
对不起,我没有这个标准的副本。
我肯定会喜欢这个的明确答案,所以有标准副本的人想要分享关于发生什么的章节和诗句:
从我的理解终止只是叫做iff:
- exception处理机制无法find引发exception的处理程序。
以下是更具体的例子:- 在堆栈展开期间,exception转义析构函数。
- 抛出的expression式,exception转义构造函数。
- exception转义非本地静态(即全局)的构造/析构函数
- exception转义了用atexit()注册的函数。
- 一个exception转义main()
- 尝试重新抛出exception,当没有exception正在传播。
- 意外的exception使用exception说明符(通过意外)