try 块

来自cppreference.com
< cpp‎ | language

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

目录

[编辑] 语法

try compound-statement handler-sequence

其中 handler-sequence 是一或多个 handler 的序列,它有下列语法:

catch ( attr(可选) type-specifier-seq declarator ) compound-statement (1)
catch ( attr(可选) type-specifier-seq abstract-declarator(可选) ) compound-statement (2)
catch ( ... ) compound-statement (3)
compound-statement - 花括号环绕的语句序列
attr(C++11) - 可选的属性列表,应用于形参
type-specifier-seq - 形参声明的一部分,与在函数参数列表中相同
declarator - 形参声明的一部分,与在函数参数列表中相同
abstract-declarator - 无名形参声明的一部分,与在函数参数列表中相同
1) 声明具名形参的 catch 子句
try { /* */ } catch (const std::exception& e) { /* */ }
2) 声明无名形参的 catch 子句
try { /* */ } catch (const std::exception&) { /* */ }
3) catch-all 处理块,对任何异常均活跃
try { /* */ } catch (...) { /* */ }

[编辑] 解释

更多关于抛出异常的信息见抛出异常

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

catch 子句的形参( type-specifier-seqdeclaratortype-specifier-seqabstract-declarator )确定何种类型的异常导致进入此 catch 子句。它不能是右值引用类型抽象类不完整类型或指向不完整类型的指针(除了允许指向(可有 cv 限定的) void )。若形参的类型是数组类型或函数类型,则它被处理成对应的指针类型(类似函数声明)。

任何 compound-statement 中的语句抛出类型 E 的异常时,它为每个 handler-seq 中的 catch 子句的形参类型 T 按 catch 子句的列出顺序相匹配。若下列任一为 true 则异常得到匹配:

  • 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 (...) 匹配任何类型的异常。若存在,则它必须是 handler-seq 中最后的 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 或通过抵达其 compound-statement 尾退出。任何这些情况下,都会销毁异常对象(除非存在指代它的 std::exception_ptr 实例)。

[编辑] 注意

throw 表达式 throw NULL; 不为捕捉指针的 catch 子句所匹配,因为异常对象的类型是 int ,但 throw nullptr; 为任何指针或指向成员指针 catch 子句所匹配。

若导出类的 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'