栈展开(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.

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

image-20211025141032175

程序执行将从 C 中的 throw 语句跳转到 main 中的 catch 语句,并在此过程中展开每个函数。

  1. 根据创建 Dummy 对象的顺序,在它们超出范围时将其销毁
  2. 除了包含 catch 语句的 main 之外,其他函数均未完成。
  3. 函数 A 绝不会从其对 B() 的调用返回,并且 B 绝不会从其对 C() 的调用返回。

reference

[1] microsoft C++文档

[2] 抛出异常与栈展开(stack unwinding)

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值