try
块
在 try 块中抛出的异常可以由关联的处理块进行处理。
语法
try 复合语句 处理块序列
|
(1) | ||||||||
try 构造函数初始化器 (可选) 复合语句 处理块序列
|
(2) | ||||||||
复合语句 | - | 复合语句 |
处理块序列 | - | 非空处理块序列 |
构造函数初始化器 | - | 成员初始化器列表(仅限构造函数) |
通常 try 块
通常 try 块是一条语句。
如果从它的复合语句 中抛出了异常,那么会将该异常与它的处理块序列 中的处理块匹配:
void f() { throw 1; // 不会被下方处理块处理 try { throw 2; // 会被关联处理块处理 } catch (...) { // 处理异常 2 } throw 3; // 不会被上方处理块处理 }
函数 try 块
函数 try 块是一类特殊的函数体。
如果从它的复合语句 或构造函数初始化器(如果存在)中抛出了异常,那么会将该异常与它的处理块序列 中的处理块匹配:
int f(bool cond) { if (cond) throw 1; return 0; } struct X { int mem; X() try : mem(f(true)) {} catch (...) { // 处理异常 1 } X(int) try { throw 2; } catch (...) { // 处理异常 2 } };
main 函数的函数 try 块不会捕获从具有静态存储期的对象的析构函数中或从与具有静态存储期的非块变量关联的对象的构造函数中抛出的异常。
线程的初始函数的函数 try 块不会捕获从具有线程存储期的对象的析构函数中或从与具有线程存储期的非块变量关联的对象的构造函数中抛出的异常。 |
(C++11 起) |
控制流抵达函数 try 块的某个处理块的复合语句 的结尾等价于抵达该函数 try 块的复合语句 的结尾,除非该函数是构造函数或析构函数(见下文)。
构造函数和析构函数 try 块
对于类 C
,如果它的构造函数或析构函数定义的函数体是一个函数 try 块,并且分别从 C
的子对象的初始化和析构中抛出了异常,那么会将该异常与该函数 try 块的处理块序列 中的处理块匹配:
int f(bool cond = true) { if (cond) throw 1; return 0; } struct X { int mem = f(); ~X() { throw 2; } }; struct Y { X mem; Y() try {} catch (...) { // 处理异常 1 } ~Y() try {} catch (...) { // 处理异常 2 } };
在某个对象构造函数或析构函数的函数 try 块的处理块中指涉该对象的非静态成员或基类会导致未定义行为。
如果构造函数的函数 try 块的处理块中出现了 return 语句,那么程序非良构。
控制流抵达构造函数或析构函数的函数 try 块的某个处理块的复合语句 的结尾时会重新抛出当前正在处理的异常。
控制流
try 块的复合语句 是有控制流限制的语句:
void f() { goto label; // 错误 try { goto label: // OK label: ; } catch (...) { goto label; // 错误 } }
跳转语句(goto
、break
、return
、continue
)可以用来将控制从 try 块(包括它的处理块)转出。此时该 try 块中声明的每个变量都会在直接包含它的声明的语境中销毁:
try { T1 t1; try { T2 t2; goto label; // 先销毁 t2,然后销毁 t1 } catch(...) { // 会在销毁 t2 的过程中抛出异常的情况下执行 } } catch(...) { // 会在销毁 t1 的过程中抛出异常的情况下执行 } label: ;
关键词
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 98 | C++98 | switch 语句可以转移控制进入 try 块的复合语句 | 已禁止 |
CWG 1167 | C++98 | 未指定析构函数的函数 try 块是否会捕获从基类析构函数或成员析构函数抛出的异常 | 会捕获这些异常 |