zoukankan      html  css  js  c++  java
  • RAII、栈展开和程序终止

    缘起

    在项目中发现某些情况下,对象的析构函数不被调用,比如程序调用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 <cstdlib>. Objects with automatic storage are NOT destroyed. Objects with static storage will be destroyed.
    • std::abort from <cstdlib>. Objects with automatic and static storage are NOT destroyed.

    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:

    • 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.)

    简单示例:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <stdexcept>   // for build-in exception, such as runtime_error
    #include <exception>   // c++ header, must use std:: to call function, such as terminate()
    
    class Test
    {
        public:
        ~Test() {
            printf("======== Call ~Test ========\n");
        }
    };
    
    static Test a;
    
    int main(int argc, char** argv)
    {
        Test t;
    
        // exit(-1);   // Objects with automatic storage are NOT destroyed. Objects with static storage will be destro
    yed.
        // _exit(-1);  // Objects with automatic and static storage are NOT destroyed.
        // abort();    // Objects with automatic and static storage are NOT destroyed.
        // std::terminate(); // Objects with automatic and static storage are NOT destroyed.
    
        // sleep(10);  // signal exit(CTRL+C). // Objects with automatic and static storage are NOT destroyed.
    
        throw std::runtime_error("test error");  // Like std::terminate()
        return 0;  // Normal. Objects with automatic and static storage are destroyed.
    }
    

    简单讲就是:除了从main函数返回之外,调用exit(), abort(), terminate()都不保证调用栈正常展开,即RAII将失效。话说回来,程序都终止了,栈不展开关系也不大,RAII失效就失效吧。但是,RAII失效意味着RAII并不能保证资源一定被释放。对于进程生存期的资源,如文件描述符(打开文件,socket等)、内存等,即使RAII失效,进程占用的资源也终将得到释放,但对于内核生存期和文件系统生存期的资源,如IPC(信号量、消息队列、共享内存)、文件等,RAII是存在缺陷的。是否有更加可靠的办法来释放系统资源呢?这个需要进一步探索。

    其实,除了上述4种程序的终止方式外,还有下面几种异常的程序终止方式:

    1)    系统调用_exit();

    2)    非法内存访问;

    3)    信号终止;

    4)    内存耗尽;

    5)    (欢迎补充)

    总之,当程序异常终止,堆栈是无需展开的,其栈上对象也将不被显示析构。

    参考:

    RAII and Stack unwinding

    相关文章:

    [Advance] How to debug a program (上)

  • 相关阅读:
    【转】css解决兼容的问题
    【转】.NET完美实现伪静态页面,URLRewriter.dll下载
    【技术贴】网站首页浏览量统计代码,适合.NET||网页浏览量计数器代码
    js过滤空格回车
    男人帮所有手机铃声下载|左永邦手机铃声下载,顾小白手机铃声下载,米琪手机铃声下载,艾米手机铃声下载
    孙红雷男人帮全集迅雷下载
    【技术帖】Mysql The 'InnoDB' feature is disabled; you n
    周杰伦2011所有歌曲完整版无删节全正版打包免费下载 http://115.com/file/bhr
    红警95 3D重置版 下载地址|3D95红警下载地址
    周杰伦2011所有歌曲完整版无删节全正版打包免费下载 http://115.com/file/bhr
  • 原文地址:https://www.cnblogs.com/zhenjing/p/stackunwinding.html
Copyright © 2011-2022 走看看