57

我希望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-bit C/C++ Optimizing Compiler Version 14.00.50727.762 for 80x86”进行编译。命令行为cl /EHa my.cpp.

编译器是否像往常一样正确?标准在这个问题上怎么说?

4

6 回答 6

75

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

C++ 规范所说的具体细节超出了我的知识范围,但使用 gdb 和 g++ 的调试跟踪似乎证实了这一点。

根据标准草案第 15.3 节第 9 条:

9 如果在程序中没有找到匹配的处理程序,函数 terminate()
  (_except.terminate_) 被调用。堆栈是否展开
  在调用 terminate() 之前是实现定义的。
于 2008-10-21T15:04:37.310 回答
18
于 2008-10-22T12:19:54.630 回答
3

抱歉,我没有标准的副本。
我肯定想要一个明确的答案,所以有标准副本的人想要分享正在发生的事情的章节和经文:

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

  • 异常处理机制找不到抛出异常的处理程序。
    以下是更具体的案例:
    • 在堆栈展开期间,异常会转义析构函数。
    • 一个抛出的表达式,一个异常转义了构造函数。
    • 异常转义非本地静态(即全局)的构造函数/析构函数
    • 异常会转义使用 atexit() 注册的函数。
    • 异常转义 main()
  • 当前没有异常传播时尝试重新抛出异常。
  • 意外异常使用异常说明符转义函数(通过意外)
于 2008-10-21T15:49:32.477 回答
2

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

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

于 2008-10-21T14:58:30.567 回答
2

我也假设编译器不会生成与“a”相关的代码,因为它没有被引用,但它仍然不是正确的行为,因为析构函数做了一些必须执行的事情。

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

现在我刚刚尝试使用 VS2005/vc8 (+SP1),它的行为是一样的。

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

于 2008-10-21T15:02:25.940 回答
2

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

确保您的异常不会越界extern "C"或使用 MSVC 选项 /EHs(启用 C++ 异常 = 是,使用外部 C 函数 (/EHs))

于 2016-05-11T13:52:46.623 回答