正常题: 抛出时栈回溯 (stack unwinding)

正常题: 抛出时栈回溯 (stack unwinding)#

抛出异常时, 控制流沿着栈 向上 进行回溯, 直到找到一个能 匹配 到的 try-catch 块, 如果没能找到, 则调用 std::terminate 终止程序.

自动存储期对象是分配在栈上的 (具体地, 分配在代码块 {} 之内), 因此当抛出异常而发生栈回溯时, 若栈回溯离开了代码块 (函数代码块、try-catch 块、自行添加的 {} 等), 则会依次析构 已构造但尚未销毁的自动存储期对象, 以它们的构造函数完成的逆序调用析构函数.

提示

如果不理解什么是栈, 请进行 断点调试, 其中:

  • 调用堆栈显示的就是栈的内容, 各行是对应函数调用的记录 (如局部变量等), 称为栈帧. 如果对递归函数进行断点调试就会发现, 每次调用都创建了新的栈帧, 即可以理解为单纯就是调用了新的函数, 所以递归函数的调用与普通函数的调用是完全一致的.

  • 局部变量窗口显示的就是栈帧中的局部变量内容, 因此当栈回溯过程离开了该局部变量所在的代码块, 自然会析构掉其中 已构造但尚未销毁的自动存储期对象.

题 1#

 1void function() {
 2  Printer c1{Info{.ctor = "c", .dtor = "n"}};
 3
 4  try {
 5    throw 1;
 6  } catch (int) {
 7    std::cout << "i";
 8  }
 9}
10
11int main() {
12  function();
13}

题 2#

try-catch 块也是代码块.

 1void function() {
 2  Printer c1{Info{.ctor = "s", .dtor = "n"}};
 3  try {
 4    Printer c2{Info{.ctor = "t", .dtor = "r"}};
 5    throw 1;
 6  } catch (int) {
 7    Printer c3{Info{.ctor = "l", .dtor = "e"}};
 8  }
 9}
10
11int main() {
12  function();
13}

题 3#

 1void function1() {
 2  Printer c1{Info{.ctor = "O", .dtor = "F"}};
 3  throw 1.0;
 4}
 5
 6void function2() {
 7  Printer* c1 = new Printer{Info{.ctor = "E", .dtor = "I"}};
 8  function1();
 9  Printer c2{Info{.ctor = "H", .dtor = "L"}};
10}
11
12int main() {
13  try {
14    function2();
15  } catch (double) {
16  }
17}

题 4#

catch 块判断是否匹配时, 与判断函数是否匹配的方式不同, 不正式地说, 它不会进行大部分隐式类型转换.

 1void function1() {
 2  try {
 3    Printer c1{Info{.ctor = "i", .dtor = "n"}};
 4    throw 1;
 5  } catch (double) {
 6  }
 7}
 8
 9void function2() {
10  Printer* c1 = new Printer{Info{.ctor = "c", .dtor = "u"}};
11  function1();
12  Printer c2{Info{.ctor = "o", .dtor = "t"}};
13}
14
15int main() {
16  try {
17    function2();
18  } catch (int) {
19  }
20}

题 5#

catch 块判断是否匹配时, 异常对象可以与其 公用基类 匹配.

 1class Base {};
 2
 3class Derived : public Base {};
 4
 5void function1() {
 6  try {
 7    Printer c1{Info{.ctor = "r", .dtor = "o"}};
 8    throw Derived{};
 9  } catch (Base&) {
10  }
11}
12
13void function2() {
14  Printer* c1 = new Printer( Info{.ctor = "f", .dtor = "z"} );
15  function1();
16  Printer c2(Info{.ctor = "n", .dtor = "t"});
17}
18
19int main() {
20  try {
21    function2();
22  } catch (Derived&) {
23  }
24}