为什么析构函数不会被异常调用?

时间:2008-10-21 14:52:17

标签: c++ exception visual-c++ destructor stack-unwinding

我希望在这个程序中调用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 ++优化编译器版本14.00.50727.762 for 80x86”进行编译。命令行为cl /EHa my.cpp

编译器是否正常?标准在这件事上说了些什么?

6 个答案:

答案 0 :(得分:65)

没有调用析构函数,因为在堆栈展开之前调用了未处理异常的terminate()。

C ++规范所说的具体细节不在我的掌握之中,但是使用gdb和g ++的调试跟踪似乎证实了这一点。

根据draft standard第15.3节第9条:

9 If no matching handler is found in a program, the function terminate()
  (_except.terminate_)  is  called.  Whether or not the stack is unwound
  before calling terminate() is implementation-defined.

答案 1 :(得分:14)

C ++语言规范声明: 为从try块到throw-expression的路径构造的自动对象调用析构函数的过程称为“堆栈展开”。 您的原始代码不包含try块,这就是为什么不会发生堆栈展开的原因。

答案 2 :(得分:2)

我也假设编译器没有生成相对于“a”的代码,因为它没有被引用但是,它仍然是正确的行为,因为析构函数会执行必须执行的操作。

所以,我尝试在VS2008 / vc9(+ SP1),调试和发布中,在抛出异常后调用~A,退出f() - 这是正确的行为,如果我是对的。< / p>

现在我尝试使用VS2005 / vc8(+ SP1)并且它的行为相同。

我使用了断点来确定。我刚用控制台检查过,我也收到了“~A”的消息。也许你在其他地方做错了?

答案 3 :(得分:2)

对不起我没有关于我的标准副本。
我肯定希望得到一个明确的答案,所以有标准副本的人想分享有关发生的章节和经文:

根据我的理解,终止只被称为iff:

  • 异常处理机制找不到抛出异常的处理程序 以下是更具体的情况:
    • 在堆栈展开期间,异常会转义析构函数。
    • 抛出的表达式,异常会转义构造函数。
    • 异常转义非本地静态(即全局)
    • 的构造函数/析构函数
    • 异常转义用atexit()注册的函数。
    • 异常转义为main()
  • 当没有异常正在传播时,尝试重新抛出异常。
  • 意外异常使用异常说明符(通过意外)转义函数

答案 4 :(得分:2)

这个问题很容易谷歌,所以我在这里分享我的情况。

确保yor exeption不跨越extern "C"边界或使用MSVC选项/ EHs(使用Extern C函数(/ EHs)启用C ++ exeptions = Yes)

答案 5 :(得分:1)

在第二个示例中,dtor在离开try {}块时被调用。

在第一个例子中,当程序在离开main()函数后关闭时调用dtor --- cout可能已经被销毁了。