CPP - 栈展开(stack unwinding)

栈展开(stack unwinding)的定义

抛出异常时,将暂停当前函数的执行,开始查找匹配的 catch 子句。首先检查 throw 本身是否在 try 块内部,如果是,检查与该 try 相关的 catch 子句,看是否可以处理该异常。如果不能处理,就退出当前函数,并且释放当前函数的内存并销毁局部对象,继续到上层的调用函数中查找,直到找到一个可以处理该异常的 catch 。这个过程称为栈展开(stack unwinding)。当处理该异常的 catch 结束之后,紧接着该 catch 之后的点继续执行。

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

    在栈展开的过程中,会释放局部对象所占用的内存并运行类类型局部对象的析构函数。但需要注意的是,如果一个块通过 new 动态分配内存,并且在释放该资源之前发生异常,该块因异常而退出,那么在栈展开期间不会释放该资源,编译器不会删除该指针,这样就会造成内存泄露。

  2. 析构函数应该从不抛出异常

    在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未经处理的另一个异常,将会导致调用标准库 terminate 函数。通常 terminate 函数将调用 abort 函数,导致程序的非正常退出。所以析构函数应该从不抛出异常。

  3. 异常与构造函数

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

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

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

例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
#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;
}

/*


*/

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ 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

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

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

reference

[1] microsoft C++文档

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

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值