Exceptions
异常处理提供了一种将控制流和信息从程序执行的某个点转移到与该执行先前经过的某个点相关联的处理器的方法(换句话说,异常处理会沿着调用栈向上传递控制权)。
评估一个 throw 表达式 会抛出异常。异常也可以在 其他上下文 中被抛出。
为了使异常能够被捕获, throw 表达式必须位于 try 块 内部,且该 try 块必须包含与异常对象类型匹配的 异常处理器 。
在声明函数时,可以提供以下规范来限制函数可能抛出的异常类型:
| (直至 C++17) |
| (since C++11) |
在异常处理期间出现的错误由 std::terminate 和 std::unexpected (C++17前) 处理。
目录 |
用法
虽然 throw 表达式可用于将控制权转移到执行堆栈上方的任意代码块(类似于 std::longjmp ),但其预期用途是错误处理。
错误处理
抛出异常用于从函数中传递错误信号,其中“错误”通常仅限于以下情况 [1] [2] [3] :
- 未能满足后置条件,例如未能生成有效的返回值对象。
- 未能满足必须调用的其他函数的前置条件。
- (针对非私有成员函数)未能(重新)建立类不变式。
具体而言,这意味着构造函数(另见 RAII )和大多数运算符的失败应通过抛出异常来报告。
此外,所谓的 宽合约 函数使用异常来表示不可接受的输入,例如, std::basic_string::at 没有前置条件,但会抛出异常来指示索引越界。
异常安全
当函数报告错误条件后,可能会对程序状态提供额外保证。通常认可以下四个层次的异常保证等级 [4] [5] [6] ,它们彼此之间存在严格的包含关系:
-
Nothrow(或无失败)异常保证
— 函数绝不抛出异常。析构函数以及在栈回溯期间可能调用的其他函数应满足Nothrow(错误通过其他方式报告或隐藏)保证。
析构函数
默认标记为
noexcept。 (C++11 起) 交换操作、 移动构造函数 以及被强异常保证函数调用的其他函数应满足Nofail(函数始终成功)保证。 - 强异常保证 — 若函数抛出异常,程序状态将回滚至函数调用前的状态(例如 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 异常 |
外部链接
|