栈展开(stack unwinding)的定义
抛出异常时,将暂停当前函数的执行,开始查找匹配的 catch
子句。首先检查 throw
本身是否在 try
块内部,如果是,检查与该 try
相关的 catch
子句,看是否可以处理该异常。如果不能处理,就退出当前函数,并且释放当前函数的内存并销毁局部对象,继续到上层的调用函数中查找,直到找到一个可以处理该异常的 catch
。这个过程称为栈展开(stack unwinding) 。当处理该异常的 catch
结束之后,紧接着该 catch
之后的点继续执行。
为局部对象调用析构函数
在栈展开的过程中,会释放局部对象所占用的内存并运行类类型局部对象的析构函数。但需要注意的是,如果一个块通过 new
动态分配内存,并且在释放该资源之前发生异常,该块因异常而退出,那么在栈展开期间不会释放该资源,编译器不会删除该指针,这样就会造成内存泄露。
析构函数应该从不抛出异常
在为某个异常进行栈展开的时候,析构函数如果又抛出自己的未经处理的另一个异常,将会导致调用标准库 terminate
函数。通常 terminate
函数将调用 abort
函数,导致程序的非正常退出。所以析构函数应该从不抛出异常。
异常与构造函数
如果在构造函数对象时发生异常,此时该对象可能只是被部分构造,要保证能够适当的撤销这些已构造的成员。
未捕获的异常将会终止程序
不能不处理异常。如果找不到匹配的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" ; B (d, i + 1 ); 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.
程序运行时对应栈的内容如下图所示:
程序执行将从 C
中的 throw 语句跳转到 main
中的 catch 语句,并在此过程中展开每个函数。
根据创建 Dummy
对象的顺序,在它们超出范围时将其销毁 。 除了包含 catch 语句的 main
之外,其他函数均未完成。 函数 A
绝不会从其对 B()
的调用返回,并且 B
绝不会从其对 C()
的调用返回。
reference
[1] microsoft C++文档
[2] 抛出异常与栈展开(stack unwinding)