天天看点

栈展开(stack unwinding)

栈展开(stack unwinding)的定义

抛出异常时,将暂停当前函数的执行,开始查找匹配的

catch

子句。首先检查

throw

本身是否在

try

块内部,如果是,检查与该

try

相关的

catch

子句,看是否可以处理该异常。如果不能处理,就退出当前函数,并且释放当前函数的内存并销毁局部对象,继续到上层的调用函数中查找,直到找到一个可以处理该异常的

catch

。这个过程称为栈展开(stack unwinding)。当处理该异常的

catch

结束之后,紧接着该

catch

之后的点继续执行。

  1. 为局部对象调用析构函数

    在栈展开的过程中,会释放局部对象所占用的内存并运行类类型局部对象的析构函数。但需要注意的是,如果一个块通过

    new

    动态分配内存,并且在释放该资源之前发生异常,该块因异常而退出,那么在栈展开期间不会释放该资源,编译器不会删除该指针,这样就会造成内存泄露。
  2. 析构函数应该从不抛出异常

    在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未经处理的另一个异常,将会导致调用标准库

    terminate

    函数。通常

    terminate

    函数将调用

    abort

    函数,导致程序的非正常退出。所以析构函数应该从不抛出异常。
  3. 异常与构造函数

    如果在构造函数对象时发生异常,此时该对象可能只是被部分构造,要保证能够适当的撤销这些已构造的成员。

  4. 未捕获的异常将会终止程序

    不能不处理异常。如果找不到匹配的catch,程序就会调用库函数

    terminate

例子

#include <string>
#include <iostream>

using namespace std;

class MyException{};
class Dummy {
public:
    // 构造函数
    Dummy(string s) : MyName(s) { PrintMsg("Created Dummy:"); }
    // 拷贝构造
    Dummy(const Dummy& other) : MyName(other.MyName){ PrintMsg("Copy created Dummy:"); }
    // 析构函数
    ~Dummy(){ PrintMsg("Destroyed Dummy:"); }
    void PrintMsg(string s) { cout << s  << MyName <<  endl; }
    string MyName;
    int level;
};

void C(Dummy d, int i) {
    cout << "Entering Function C" << endl;
    d.MyName = " C";
    throw MyException();

    cout << "Exiting Function C" << endl;
}

void B(Dummy d, int i) {
    cout << "Entering Function B" << endl;
    d.MyName = " B";
    C(d, i + 1);
    cout << "Exiting Function B" << endl;
}

void A(Dummy d, int i) {
    cout << "Entering Function A" << endl;
    d.MyName = " A" ;
  //  Dummy* pd = new Dummy("new Dummy"); //Not exception safe!!!
    B(d, i + 1);
 //   delete pd;
    cout << "Exiting FunctionA" << endl;
}

int main() {
    cout << "Entering main" << endl;
    try {
        Dummy d(" M");
        A(d,1);
    }
    catch (MyException& e) {
        cout << "Caught an exception of type: " << typeid(e).name() << endl;
    }
    cout << "Exiting main." << endl;
    return 0;
}

/*


*/
           

进行编译,运行,可得到如下结果:

$ g++ stack_unwinding.cpp -o stack_test -std=c++11

$ ./stack_test                                    
Entering main
Created Dummy: M
Copy created Dummy: M
Entering Function A
Copy created Dummy: A
Entering Function B
Copy created Dummy: B
Entering Function C
Destroyed Dummy: C
Destroyed Dummy: B
Destroyed Dummy: A
Destroyed Dummy: M
Caught an exception of type: 11MyException
Exiting main.
           

程序运行时对应栈的内容如下图所示:

栈展开(stack unwinding)
  1. 根据创建

    Dummy

    对象的顺序,在它们超出范围时将其销毁。
  2. 除了包含 catch 语句的

    main

    之外,其他函数均未完成。
  3. 函数

    A

    绝不会从其对

    B()

    的调用返回,并且

    B

    C()

    的调用返回。

reference

继续阅读