缘起
在项目中发现某些情况下,对象的析构函数不被调用,比如程序调用exit(), 异常终止等。那么,析构函数什么情况下不会被调用呢?RAII
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:
- Return from main. Objects with automatic storage (function-local) have already been destroyed. Objects with static storage (global, class-static, function-static) will be destroyed.
- std::exit from
. Objects with automatic storage are NOT destroyed. Objects with static storage will be destroyed. - std::abort from
. Objects with automatic and static storage are NOT destroyed.
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:
- Avoid std::abort.
- Either avoid std::exit or avoid all objects with static storage duration.
- Put a catch (...) handler in main, and make sure no allocations or exceptions happen in or after it.
- Avoid the other programming errors that can cause std::terminate.
- (On some implementations, functions compiled with a C compiler act like they have C++'s empty throw() specification, meaning that exceptions cannot be thrown "past" them even though they have no destructors to be called.)
其实,除了上述4种程序的终止方式外,还有下面几种异常的程序终止方式:
1) 系统调用_exit();
2) 非法内存访问;
3) 信号终止;
4) 内存耗尽;
5) (欢迎补充)
总之,当程序异常终止,堆栈是无需展开的,其栈上对象也将不被显示析构。
参考:
RAII and Stack unwinding
相关文章:
[Advance] How to debug a program (上)
最新评论