异常
异常处理提供了一种将控制和信息从程序执行中的某个点传输到与先前执行点相关联的处理程序(换句话说,异常处理将控制权沿调用栈向上传输)的方法。
评估 throw 表达式 将抛出异常。异常也可以在其他上下文中抛出。
为了捕获异常,throw 表达式必须在 try 块内,并且 try 块必须包含与异常对象类型匹配的处理程序。
声明函数时,可以提供以下规范以限制函数可能抛出的异常类型
(C++17 前) |
(C++11 起) |
异常处理期间发生的错误由 std::terminate 和 std::unexpected(C++17 前) 处理。
目录 |
[编辑] 用法
虽然 throw 表达式可用于出于任意原因将控制权传输到执行栈上方的任意代码块(类似于 std::longjmp),但其预期用途是错误处理。
[编辑] 错误处理
抛出异常用于从函数中发出错误信号,其中“错误”通常仅限于以下几点[1][2][3]
- 未能满足后置条件,例如未能生成有效的返回值对象。
- 未能满足必须调用的另一个函数的前置条件。
- (对于非私有成员函数)未能(重新)建立类不变式。
特别地,这意味着构造函数(参见 RAII)和大多数运算符的失败应通过抛出异常来报告。
此外,所谓的“宽契约”函数使用异常来指示不可接受的输入,例如,std::basic_string::at 没有前置条件,但抛出异常来指示索引超出范围。
[编辑] 异常安全
函数报告错误条件后,可能会提供关于程序状态的额外保证。通常认可以下四级异常保证[4][5][6],它们是彼此的严格超集
- 不抛出(或不失败)异常保证 — 函数从不抛出异常。不抛出(错误通过其他方式报告或隐藏)是析构函数和其他在栈展开期间可能调用的函数的期望。析构函数默认是
noexcept
的。(C++11 起) 不失败(函数总是成功)是交换操作、移动构造函数以及提供强异常保证的函数所使用的其他函数的期望。 - 强异常保证 — 如果函数抛出异常,程序的状态将回滚到函数调用之前的状态(例如,std::vector::push_back)。
- 基本异常保证 — 如果函数抛出异常,程序处于有效状态。没有资源泄漏,并且所有对象的不变式都完整无损。
- 无异常保证 — 如果函数抛出异常,程序可能处于无效状态:可能发生资源泄漏、内存损坏或其他破坏不变式的错误。
通用组件还可以提供异常中立保证:如果从模板参数(例如,从 std::sort 的 Compare
函数对象或从 std::make_shared 中 T
的构造函数)抛出异常,则该异常将未经修改地传播给调用者。
[编辑] 异常对象
虽然任何完整类型的对象和指向 void 的 cv 指针都可以作为异常对象抛出,但所有标准库函数都通过值抛出未命名对象,并且这些对象的类型直接或间接派生自 std::exception。用户定义的异常通常遵循此模式。[7][8][9]
为避免不必要的异常对象复制和对象切片,处理程序的最佳实践是通过引用捕获。[10][11][12][13]
[编辑] 注意
功能测试宏 | 值 | 标准 | 特性 |
---|---|---|---|
__cpp_constexpr_exceptions |
202411L |
(C++26) | constexpr 异常 |
[编辑] 外部链接
|