为什么异常时不调用析构函数

Why destructor is not called on exception?

本文关键字:调用 析构函数 异常 为什么      更新时间:2023-10-16

我希望在这个程序中调用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

编译器像往常一样正确吗?标准对此事有何规定?

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

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

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

9 如果在程序中找不到匹配的处理程序,则函数 terminate()  (_except.终止_) 被调用。 堆栈是否展开  在调用 terminate() 之前是实现定义的。

C++语言规范指出:为从 try 块到抛出表达式的路径上构造的自动对象调用析构函数的过程称为"堆栈展开"。您的原始代码不包含 try 块,这就是为什么堆栈展开不会发生的原因。

抱歉,

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

据我了解,终止只叫 iff:

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

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

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

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

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

现在我刚刚尝试使用VS2005/vc8(+SP1),这是相同的行为。

我使用断点来确定。我刚刚检查了控制台,我也有"~A"消息。也许你在别的地方做错了?

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

确保 yor exeption 不会越过边界extern "C"或使用 MSVC 选项/EH(使用 Extern C 函数 (/EH) 启用 C++ exeptions = 是)