提问人:Luchian Grigore 提问时间:11/29/2011 最后编辑:Luchian Grigore 更新时间:6/19/2019 访问量:27284
析构函数是在 C++ 抛出后调用的吗?
Are destructors called after a throw in C++?
答:
是的,它是有保证的(前提是捕获到异常),直到调用析构函数的顺序:
C++11 15.2 构造函数和析构函数 [except.ctor]
1 当控制从抛出表达式传递到处理程序时,将对所有 自输入 try 块以来构造的自动对象。这 自动对象以与完成的相反顺序销毁 他们的建筑。
此外,如果在对象构造过程中抛出异常,则保证部分构造对象的子对象被正确销毁:
2 任何存储持续时间的对象,其初始化或 销毁被异常终止时将有析构函数 针对其所有完全构造的子对象执行(不包括 类联合类的变体成员),即用于 主构造函数 (12.6.2) 已完成执行,并且 析构函数尚未开始执行。同样,如果 对象的非委托构造函数已完成执行,并且 委托该对象的构造函数退出,并出现异常, 对象的析构函数将被调用。如果对象是在 new-expression,匹配的释放函数(3.7.4.2、5.3.4、 12.5) (如果有)以释放对象占用的存储空间。
这整个过程被称为“堆垛放卷”:
3 自动构造对象调用析构函数的过程 在从 try 块到 throw-expression 的路径上称为“堆栈” 放松。如果在堆栈展开期间调用的析构函数退出 异常 std::terminate 被调用 (15.5.1)。
堆栈展开构成了广泛使用的称为资源获取即初始化 (RAII) 的技术的基础。
请注意,如果未捕获异常,则不一定执行堆栈展开。在这种情况下,是否完成堆栈展开取决于实现。但是,无论是否完成堆栈展开,在这种情况下,您都可以保证最终调用 .std::terminate
C++11 15.5.1 std::terminate() 函数 [except.terminate]
2 ...在未找到匹配处理程序的情况下, 它是否在调用堆栈之前展开,都是由实现定义的。
std::terminate()
评论
是的,析构函数保证在堆栈展开时被调用,包括由于抛出异常而展开。只有少数技巧是您必须记住的例外:
- 如果在其构造函数中引发异常,则不会调用类的析构函数。
- 如果在构造初始化列表捕获块中捕获异常,则会自动重新抛出异常。
评论
terminate()
release
如果投掷被捕获,则通常 cpp 操作将继续。这包括析构函数和堆栈弹出。但是,如果未捕获异常,则不能保证堆栈弹出。
此外,我的移动编译器无法捕获裸抛或空抛。
例:
#include <Jav/report.h>
int main()
{
try { throw; }
catch(...) { rep("I bet this is not caught"); }
}
评论