命名空间
变体
操作

处理异常

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句(循环)
for
范围 for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (直至 C++17*)
noexcept 说明符 (C++11)
异常

catch 处理程序
命名空间
类型
说明符
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
存储期说明符
初始化
 
 
异常
try
抛出异常
处理异常
异常规范
    noexcept 规范 (C++11)
    动态规范 (直至 C++17*)
noexcept 运算符 (C++11)
 

异常可以由处理程序处理。

目录

[[编辑]] 处理程序

catch ( attr (可选) 类型说明符序列 声明符 ) 复合语句 (1)
catch ( attr (可选) 类型说明符序列 抽象声明符 (可选) ) 复合语句 (2)
catch ( ... ) 复合语句 (3)
1) 带有具名形参的处理程序。
2) 带有无名形参的处理程序。
3) 匹配所有种类异常的处理程序。
attr - (自 C++11 起) 任意数量的 属性,应用于形参
type-specifier-seq - 形式形参声明的一部分,与函数 形参列表 中相同
declarator - 形参声明的一部分,与函数 形参列表 中相同
abstract-declarator - 无名形参声明的一部分,与函数 形参列表 中相同
compound-statement - 复合语句


处理程序中的形参声明描述了可以导致进入该处理程序的异常类型。

如果形参声明为以下类型之一,则程序是非良构的

(自 C++11 起)
  • 指向不完整类型的指针,但 (可能带 cv 限定的) void 除外
  • 到不完整类型的左值引用

如果形参声明为 “T 的数组” 类型或函数类型 T,则类型被调整为 “指向 T 的指针”。

带有形参类型 T 的处理程序可以缩写为 “T 类型的处理程序”。

[[编辑]] 匹配异常

每个 try 块都与若干个处理程序关联,这些处理程序构成一个处理程序序列。当从 try 块抛出异常时,将按出现顺序尝试序列中的处理程序以匹配异常。

如果满足以下任一条件,则处理程序是类型为 E异常对象的匹配项

  • 处理程序的类型为 “可能带 cv 限定的 T” 或 “到可能带 cv 限定的 T 的左值引用”,并且满足以下任一条件
  • ET 是相同类型(忽略顶层 cv 限定符)。
  • TE 的无歧义公开基类。
  • 处理程序的类型为 “可能带 cv 限定的 T” 或 const T&,其中 T 是指针或指向成员类型的指针,并且满足以下任一条件
  • E 是指针或指向成员类型的指针,可以通过以下至少一种转换转换为 T
(自 C++17 起)
(自 C++11 起)

catch (...) 处理程序匹配任何类型的异常。如果存在,则它只能是处理程序序列中的最后一个处理程序。此处理程序可用于确保没有未捕获的异常可能从提供不抛出异常保证的函数中逃脱。

try
{
    f();
}
catch (const std::overflow_error& e)
{} // this executes if f() throws std::overflow_error (same type rule)
catch (const std::runtime_error& e)
{} // this executes if f() throws std::underflow_error (base class rule)
catch (const std::exception& e)
{} // this executes if f() throws std::logic_error (base class rule)
catch (...)
{} // this executes if f() throws std::string or int or any other unrelated type

如果在 try 块的处理程序中找不到匹配项,则在动态包围的 try(同一线程的)(自 C++11 起)中继续搜索匹配的处理程序。

如果找不到匹配的处理程序,则调用 std::terminate;在调用 std::terminate 之前是否栈回溯是实现定义的。

[[编辑]] 处理异常

当抛出异常时,控制权转移到具有匹配类型的最近的处理程序;“最近” 是指由控制线程最近进入且尚未退出的 try 关键字后的复合语句或成员初始化列表(如果存在)的处理程序。

[[编辑]] 初始化处理程序形参

在形参列表(如果有)中声明的类型为 “可能带 cv 限定的 T” 或 “到可能带 cv 限定的 T 的左值引用” 的形参,按如下方式从类型为 E异常对象初始化

  • 如果 TE 的基类,则形参从类型为 T 的左值复制初始化,该左值指定异常对象的相应基类子对象。
  • 否则,形参从类型为 E 的左值复制初始化,该左值指定异常对象。

形参的生存期在处理程序退出时结束,在处理程序内初始化的任何具有自动存储期的对象销毁之后。

当形参声明为对象时,对该对象的任何更改都不会影响异常对象。

当形参声明为对对象的引用时,对被引用对象的任何更改都是对异常对象的更改,并且如果该对象被重新抛出,则会生效。

[[编辑]] 激活处理程序

当处理程序的形参(如果有)的初始化完成时,处理程序被认为是活动的

此外,当由于抛出而进入 std::terminate 时,隐式处理程序也被认为是活动的。

当处理程序退出时,不再认为处理程序是活动的。

最近激活且仍处于活动状态的处理程序的异常称为当前处理的异常。这样的异常可以被重新抛出

[[编辑]] 控制流

处理程序的 复合语句控制流限制语句

void f()
{
    goto label;     // error
    try
    {
        goto label; // error
    }
    catch (...)
    {
        goto label: // OK
        label: ;
    }
}

[[编辑]] 注解

栈回溯发生在控制权转移到处理程序时。当处理程序变为活动状态时,栈回溯已经完成。

throw 表达式 throw 0 抛出的异常与指针或指向成员类型的指针的处理程序不匹配。

  • throw nullptr 可以用来代替抛出空指针,该空指针与此类处理程序匹配。
(自 C++11 起)

异常对象永远不能具有数组或函数类型,因此,到数组或函数类型的引用的处理程序永远不会与任何异常对象匹配。

可以编写永远不会执行的处理程序,例如,在对应的无歧义公共基类的处理程序之后放置最终派生类的处理程序

try
{
    f();
}
catch (const std::exception& e)
{} // will be executed if f() throws std::runtime_error
catch (const std::runtime_error& e)
{} // dead code!

许多实现过度扩展了 CWG 问题 388 对非 const 指针类型引用的处理程序的分辨率

int i;
try
{
    try
    {
        throw static_cast<float*>(nullptr);
    }
    catch (void*& pv)
    {
        pv = &i;
        throw;
    }
}
catch (const float* pf)
{
    assert(pf == nullptr); // should pass, but fails on MSVC and Clang
}

[[编辑]] 关键字

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() throws std::out_of_range
    }
    catch (const std::exception& e) // caught by reference to base
    {
        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'

[[编辑]] 缺陷报告

以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。

DR 应用于 已发布行为 正确行为
CWG 98 C++98 switch 语句可以将控制权转移到处理程序中 禁止
CWG 210 C++98 throw 表达式与处理程序匹配 异常对象是
与处理程序匹配
CWG 388 C++98 指针或指向成员类型的指针的异常可能
无法通过到不同类型的 const 引用的处理程序匹配
变为可匹配
当可转换时
CWG 1166 C++98 当处理程序的行为未指定时
类型是对抽象类类型的引用时匹配
抽象类类型是
不允许用于处理程序
CWG 1769 C++98 当处理程序的类型是异常对象的类型的基类时
转换构造函数可能
用于处理程序形参的初始化
形参被复制初始化
来自异常对象的相应基类
子对象
CWG 2093 C++98 指向对象类型的指针的异常对象无法通过限定转换匹配指向对象类型的指针的处理程序
允许

[[编辑]] 参考

  • C++23 标准 (ISO/IEC 14882:2024)
  • 14.4 处理异常 [except.handle]
  • C++20 标准 (ISO/IEC 14882:2020)
  • 14.4 处理异常 [except.handle]
  • C++17 标准 (ISO/IEC 14882:2017)
  • 18.3 处理异常 [except.handle]
  • C++14 标准 (ISO/IEC 14882:2014)
  • 15.3 处理异常 [except.handle]
  • C++11 标准 (ISO/IEC 14882:2011)
  • 15.3 处理异常 [except.handle]
  • C++03 标准 (ISO/IEC 14882:2003)
  • 15.3 处理异常 [except.handle]
  • C++98 标准 (ISO/IEC 14882:1998)
  • 15.3 处理异常 [except.handle]

[[编辑]] 参见