本文共 2789 字,大约阅读时间需要 9 分钟。
在项目中发现某些情况下,对象的析构函数不被调用,比如程序调用exit(), 异常终止等。那么,析构函数什么情况下不会被调用呢?
RAII(资源获取即初始化RAII, Resource Acquisition Is Initialization)是C++编程中很重要的一项技术。其原理是在对象析构函数中释放该对象获取的资源,利用栈展开过程栈上对象的析构函数将被自动调用的保证,从而正确地释放先前获取的资源。RAII只有在栈展开正常执行的前提下才能正常工作。函数调用和正常的C++异常处理流程(异常处于try-catch块)都存在栈展开。
最常见的栈展开就是正常的函数调用,任何一个函数返回都存在栈展开。C++引入异常机制后,当程序抛出异常,在异常向上传递的过程中,其函数调用栈也会展开。
先摘录一段来自stackoverflow的回答:
The Standard defines three ways to end execution of a C++ program:
Also relevant is std::terminate from <exception>. The behavior of terminate can be replaced using std::set_terminate, but terminate must always "terminate execution of the program" by calling abort or some similar implementation-specific alternative. The default is just { std::abort(); }.
C++ will call std::terminate whenever an exception is thrown and C++ can't reasonably do stack unwinding. For example, an exception from a destructor called by stack unwinding or an exception from a static storage object constructor or destructor. In these cases, there is no (more) stack unwinding done.
C++ will also call std::terminate when a matching catch handler is not found. In this single case, C++ may optionally unwind to main before calling terminate. So your example might have different results with a different compiler.(implementation-defined)
So if you use RAII correctly, the remaining steps to "leak-proof" your program are:
简单示例:
简单讲就是:除了从main函数返回之外,调用exit(), abort(), terminate()都不保证调用栈正常展开,即RAII将失效。话说回来,程序都终止了,栈不展开关系也不大,RAII失效就失效吧。但是,RAII失效意味着RAII并不能保证资源一定被释放。对于进程生存期的资源,如文件描述符(打开文件,socket等)、内存等,即使RAII失效,进程占用的资源也终将得到释放,但对于内核生存期和文件系统生存期的资源,如IPC(信号量、消息队列、共享内存)、文件等,RAII是存在缺陷的。是否有更加可靠的办法来释放系统资源呢?这个需要进一步探索。
其实,除了上述4种程序的终止方式外,还有下面几种异常的程序终止方式:
1) 系统调用_exit();
2) 非法内存访问;
3) 信号终止;
4) 内存耗尽;
5) (欢迎补充)
总之,当程序异常终止,堆栈是无需展开的,其栈上对象也将不被显示析构。
参考:
相关文章:
本文转自 zhenjing 博客园博客,原文链接: http://www.cnblogs.com/zhenjing/archive/2011/07/06/stackunwinding.html ,如需转载请自行联系原作者