函数 try 块
建立围绕整个函数体的异常处理块。
语法
函数 try 块是一种 函数体 的替代语法形式,它是函数定义的一部分。
try 构造函数初始化器(可选) 复合语句 处理块序列
|
|||||||||
构造函数初始化器 | - | 成员初始化器列表,只能用于构造函数 |
复合语句 | - | 花括号环绕的语句序列,它组成函数体 |
处理块序列 | - | 一或多个 catch 子句的序列 |
解释
函数 try 块将一系列 catch 子句与整个函数体,以及成员初始化器列表(如果用于构造函数)关联起来。从函数体中的任何语句,或(对于构造函数)从任何成员或基类的构造函数,或(对于析构函数)从任何成员或基类的析构函数中抛出的所有异常,以与常规 try 块中抛出的异常时相同方式,将控制转移到 处理块序列 中。
#include <iostream> #include <string> struct S { std::string m; S(const std::string& str, int idx) try : m(str, idx) { std::cout << "S(" << str << ", " << idx << ") 构造完成,m = " << m << '\n'; } catch (const std::exception& e) { std::cout << "S(" << str << ", " << idx << ") 失败:" << e.what() << '\n'; } // 此处有隐含的 "throw;" }; int main() { S s1{"ABC", 1}; // 不抛出(索引在范围内) try { S s2{"ABC", 4}; // 抛出(越界) } catch (std::exception& e) { std::cout << "S s2... 抛出了一个异常:" << e.what() << '\n'; } }
在进入任何构造函数上的函数 try 块的 catch 子句前,所有完整构造的成员和基类都会被销毁。
如果函数 try 块在委托构造函数上,而它调用的非委托构造函数成功完成,但之后该委托构造函数的函数体抛出了异常,那么此对象的析构函数将在进入函数 try 块的任何 catch 子句之前完成。 |
(C++11 起) |
在进入析构函数上的函数 try 块的任何 catch 子句之前,所有基类和非变体成员均已被销毁。
如果用于构造函数或析构函数上的函数 try 块的 catch 子句访问了基类或对象的非静态成员,那么行为未定义。
构造函数的函数 try 块中的每个 catch 子句都必须以抛异常终止。如果控制抵达这种异常处理的尾部,那么当前异常被如同用 throw; 来自动重抛。在构造函数的函数 try 块的任何 catch 子句中都不能使用 return 语句。
抵达析构函数的函数 try 块的 catch 子句末尾,也如同用 throw; 来自动重抛当前异常,但可以使用 return 语句。
对于所有其他函数,如果函数返回类型是(可有 cv 限定的)void,那么抵达 catch 子句末尾等价于 return;,否则行为未定义。
注解
函数 try 块的主要目的是应对从构造函数中的成员初始化器列表抛出的异常,进行记录并重抛,修改异常对象并重抛,抛出一个不同的异常,或终止程序。析构函数或常规函数很少用到它们。
函数 try 块不会捕获从按值传递的函数形参的复制/移动构造函数和析构函数中抛出的异常:这些异常是在调用方的语境抛出的。
线程的顶层函数的函数 try 块不会捕获从线程局部对象的构造函数和析构函数(但除了函数作用域的线程局域对象的构造函数)中抛出的异常。 |
(C++11 起) |
类似地,main() 函数的函数 try 块不会捕获从静态对象的构造函数和析构函数(但除了函数局域静态对象的构造函数)中抛出的异常。
函数形参(但并非在函数自身中声明的任何对象)的作用域和生存期延续到 处理块序列 的结尾。
int f(int n = 2) try { ++n; // 使函数形参自增 throw n; } catch(...) { ++n; // n 处于作用域中且仍然指代函数形参 assert(n == 4); return n; }
缺陷报告
下列更改行为的缺陷报告追溯地应用于以前出版的 C++ 标准。
缺陷报告 | 应用于 | 出版时的行为 | 正确行为 |
---|---|---|---|
CWG 1167 | C++98 | 未指明析构函数的函数 try 块是否会捕获从基类析构函数或成员析构函数抛出的异常 | 会捕获这些异常 |