析构函数中的 C++ 异常
c++ exception in destructor
从其他线程中,我知道我们不应该在析构函数中抛出异常!但对于下面的例子,它确实有效。这是否意味着我们只能在一个实例的析构函数中抛出异常?我们应该如何理解这个代码示例!
#include <iostream>
using namespace std;
class A {
public:
~A() {
try {
printf("exception in A startn");
throw 30;
printf("exception in A endn");
}catch(int e) {
printf("catch in A %dn",e);
}
}
};
class B{
public:
~B() {
printf("exception in B startn");
throw 20;
printf("exception in B endn");
}
};
int main(void) {
try {
A a;
B b;
}catch(int e) {
printf("catch in main %dn",e);
}
return 0;
}
输出为:
exception in B start
exception in A start
catch in A 30
catch in main 20
C++17 之前的最佳实践是不要让异常从析构函数中传播出去。如果析构函数包含throw
表达式或调用可能引发的函数,只要捕获并处理引发的异常而不是从析构函数中转义即可。 所以你的A::~A
很好。
在B::~B
的情况下,您的程序在 C++03 中很好,但在 C++11 中则不行。规则是,如果您确实让异常从析构函数中传播出来,并且该析构函数用于通过堆栈展开直接销毁的自动对象,则将调用std::terminate
。由于b
不会作为堆栈展开的一部分被销毁,因此将捕获从B::~B
抛出的异常。但是在 C++11 中,B::~B
析构函数将被隐式声明为noexcept
,因此,允许异常从中传播出来将无条件地调用std::terminate
。
要允许在 C++11 中捕获异常,您可以编写
~B() noexcept(false) {
// ...
}
尽管如此,还是会出现一个问题,即在堆栈展开期间可能会调用B::~B
---在这种情况下,将调用std::terminate
。由于在 C++17 之前,无法判断是否是这种情况,因此建议永远不允许异常从析构函数中传播。遵循这个规则,你会没事的。
在C++17中,可以使用std::uncaught_exceptions()
来检测在堆叠放卷过程中物体是否被破坏。但你最好知道你在做什么。
"我们不应该在析构函数中抛出异常"的建议不是绝对的。问题是,当引发异常时,编译器开始展开堆栈,直到找到该异常的处理程序。展开堆栈意味着为由于堆栈帧即将消失而消失的对象调用析构函数。如果其中一个析构函数抛出未在析构函数本身中处理的异常,则会发生此建议的内容。如果发生这种情况,程序会调用std::terminate()
,有些人认为发生这种情况的风险非常严重,以至于他们必须编写编码指南来防止这种情况发生。
在您的代码中,这不是问题。B
的析构函数引发异常;因此,还会调用a
的析构函数。该析构函数引发异常,但在析构函数内处理异常。所以没有问题。
如果更改代码以删除A
析构函数中的try ... catch
块,则析构函数中引发的异常不会在析构函数中处理,因此最终会调用std::terminate()
。
编辑:正如Brian在他的回答中指出的那样,这个规则在C++11中发生了变化:析构函数是隐式noexcept
的,所以你的代码应该在B
对象被销毁时调用terminate
。将析构函数标记为noexcept(false)
"修复"此问题。
- 处理多个异常集合的C++方法
- 我在c++代码中生成了一个运行时#3异常
- 孤立代码块在结构中引发异常
- C++中的赋值发生,尽管右侧出现异常
- 从构造函数抛出异常时如何克服内存泄漏
- 异常属于C++中的线程还是进程
- 当类定义不可见时捕获异常
- 引发异常:读取访问冲突**dynamicArray**为0x1118235.发生
- 为什么异常不退出程序?
- 为什么我应该在异常处理中使用std::cerr而不是std::cout
- 如何修复链表类实现的未处理异常0xDDDDDDDD
- 关于:C++中异常对象的范围:为什么我没有得到副本?
- 是什么导致了Unity 3D中的"错误线程异常"?
- 如何将strftime中的格式错误作为异常捕获
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 尝试使用智能指针时引发异常
- 函数如何通知用户它基于函数原型抛出异常?
- 是否值得降低我的代码的可读性,以便在出现内存不足错误时提供异常安全性?
- 当我使用 C++ 中的 C# dll 来使用 Selenium 时,存在异常处理问题
- Pytorch torch.cholesky忽略异常