try 块

< cpp‎ | language

将一或多个异常处理块(catch 子句)与复合语句关联。

语法

try 复合语句 处理块序列

其中 处理块序列 是一或多个 处理块 的序列,它有下列语法:

catch ( attr(可选) 类型说明符序列 声明符 ) 复合语句 (1)
catch ( attr(可选) 类型说明符序列 抽象声明符(可选) ) 复合语句 (2)
catch ( ... ) 复合语句 (3)
复合语句 - 花括号环绕的语句序列
attr(C++11) - 可选的属性列表,应用于形参
类型说明符序列 - 形参声明的一部分,与在函数形参列表中相同
声明符 - 形参声明的一部分,与在函数形参列表中相同
抽象声明符 - 无名形参声明的一部分,与在函数形参列表中相同
1) 声明一个具名形参的 catch 子句
try { /* */ } catch (const std::exception& e) { /* */ }
2) 声明一个无名形参的 catch 子句
try { /* */ } catch (const std::exception&) { /* */ }
3) catch-all 处理块,可被任何异常激活
try { /* */ } catch (...) { /* */ }

解释

更多关于 throw 表达式的信息见抛出异常

try 块是一条语句,从而能出现于任何语句所能出现处(即作为复合语句中的语句之一,包含函数体复合语句)。有关围绕函数体的 try 块,见函数 try 块。下列描述同时适用于 try 块和函数 try 块

catch 子句的形参(类型说明符序列声明符,或者 类型说明符序列抽象声明符)决定何种类型的异常导致进入此 catch 子句。它不能是右值引用类型抽象类不完整类型或指向不完整类型的指针(但允许指向(可有 cv 限定的)void 的指针)。若形参的类型是数组类型或函数类型,则它被处理成对应的指针类型(类似于函数声明)。

复合语句 中的任何语句抛出了类型 E 的异常时,以之对 处理块序列 中的每个 catch 子句的形参类型 T,按 catch 子句的列出顺序进行匹配。若下列任一为真则异常得到匹配:

  • ET 是同一类型(忽略 T 上的顶层 cv 限定符)
  • T 是到(可为 cv 限定的)E 的左值引用
  • TE 的无歧义公开基类
  • T 是到 E 的无歧义公开基类的引用
  • T 是(可为 cv 限定的)U const U& (C++14 起),且 U 是指针或成员指针 (C++17 起)类型,且 E 亦为能通过下列转换中的一或多个转换成 U 的指针或成员指针 (C++17 起)类型
(C++17 起)
  • T 是指针或成员指针,或 const 指针的引用 (C++14 起),而 Estd::nullptr_t.
try {
    f();
} catch (const std::overflow_error& e) {
    // 若 f() 抛出 std::overflow_error 则执行之(“相同类型”规则)
} catch (const std::runtime_error& e) {
    // 若 f() 抛出 std::underflow_error 则执行之(“基类”规则)
} catch (const std::exception& e) {
    // 若 f() 抛出 std::logic_error 则执行之(“基类”规则)
} catch (...) {
    // 若 f() 抛出 std::string 或 int 或任何其他无关类型则执行之
}

全捕获(catch-all)子句 catch (...) 匹配任何类型的异常。若存在,则它必须是 处理块序列 中的最后一个 catch 子句。Catch-all 块可用于确保不可能有未捕获异常从提供不抛出异常保证的函数逃逸。

若检测所有 catch 子句后仍无匹配,则到外围的 try 块继续异常的传播,如 throw 表达式中所述。若没有剩下的外围 try 块,则执行 std::terminate(此情况下,由实现定义是否完全进行栈回溯:容许抛出未捕获异常导致程序终止而不调用任何析构函数)。

当进入一个 catch 子句时,若其形参是异常类型的基类,则它从异常对象的基类子对象进行复制初始化。否则,它从异常对象复制初始化(这个复制服从复制消除规则)。

try {
   std::string("abc").substr(10); // 抛出 std::length_error
// } catch (std::exception e) { // 从 std::exception 基类复制初始化
//     std::cout << e.what(); // 丢失来自 length_error 的信息
// }
} catch (const std::exception& e) { // 多态对象基类的引用
     std::cout << e.what(); // 打印来自 length_error 的信息
}

若 catch 子句的形参是引用类型,则对它所做的任何更改都会反映到异常对象之中,且若以 throw; 重抛这个异常,则可为另一处理块所观测到。若形参不是引用,则任何对它的更改都是局域的,且其生存期在处理块退出时结束。

在 catch 子句内,可以使用 std::current_exception 把异常捕获到一个 std::exception_ptr 之中,而且可以使用 std::throw_with_nested 来构建嵌套的异常。

(C++11 起)

除了抛出或重抛异常以外,普通的 try 块(非函数 try 块)之后的 catch 子句还可以通过 returncontinuebreakgoto,或通过抵达其 复合语句 尾而退出。任何这些情况,都会销毁异常对象(除非存在指代它的 std::exception_ptr 实例)。

注解

不保证捕捉指针的 catch 子句能够匹配 throw 表达式 throw NULL;,因为异常对象类型可以是整数类型,但可以确保任何指针或成员指针的 catch 子句能够匹配 throw nullptr;

若派生类的 catch 子句被置于基类的 catch 子句后,则永远不会执行派生类 catch 子句。

try {
    f();
} catch (const std::exception& e) {
    // 若 f() 抛 std::runtime_error 则执行
} catch (const std::runtime_error& e) {
    // 死代码!
}

若用 goto 退出 try 块,且若这个 goto 所执行的任何块作用域自动变量的析构函数由于抛异常而退出,则那些异常被该变量定义于其中的 try 块所捕捉:

label:
    try {
        T1 t1;
        try {
            T2 t2;
            if(condition)
                goto label; // 销毁 t2,然后销毁 t1,再跳到 label
        } catch (...) {  } // 捕捉来自 t2 析构函数的异常
    } catch (...) {  } // 捕捉来自 t1 析构函数的异常

关键词

try, catch, throw

示例

下列代码演示 try-catch 块的数种用法

#include <iostream>
#include <vector>
 
int main() {
    try {
        std::cout << "Throwing an integer exception...\n";
        throw 42;
    } catch (int i) {
        std::cout << " the integer exception was caught, with value: " << i << '\n';
    }
 
    try {
        std::cout << "Creating a vector of size 5... \n";
        std::vector<int> v(5);
        std::cout << "Accessing the 11th element of the vector...\n";
        std::cout << v.at(10); // vector::at() 抛出 std::out_of_range
    } catch (const std::exception& e) { // 按基类的引用捕获
        std::cout << " a standard exception was caught, with message '"
                  << e.what() << "'\n";
    }
 
}

可能的输出:

Throwing an integer exception...
 the integer exception was caught, with value: 42
Creating a vector of size 5...
Accessing the 11th element of the vector...
 a standard exception was caught, with message 'out_of_range'