Namespaces
Variants

Exceptions

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

异常处理提供了一种将控制流和信息从程序执行的某个点转移到与该执行先前经过的某个点相关联的处理器的方法(换句话说,异常处理会沿着调用栈向上传递控制权)。

评估一个 throw 表达式 会抛出异常。异常也可以在 其他上下文 中被抛出。

为了使异常能够被捕获, throw 表达式必须位于 try 内部,且该 try 块必须包含与异常对象类型匹配的 异常处理器

在声明函数时,可以提供以下规范来限制函数可能抛出的异常类型:

(直至 C++17)
(since C++11)

在异常处理期间出现的错误由 std::terminate std::unexpected (C++17前) 处理。

目录

用法

虽然 throw 表达式可用于将控制权转移到执行堆栈上方的任意代码块(类似于 std::longjmp ),但其预期用途是错误处理。

错误处理

抛出异常用于从函数中传递错误信号,其中“错误”通常仅限于以下情况 [1] [2] [3]

  1. 未能满足后置条件,例如未能生成有效的返回值对象。
  2. 未能满足必须调用的其他函数的前置条件。
  3. (针对非私有成员函数)未能(重新)建立类不变式。

具体而言,这意味着构造函数(另见 RAII )和大多数运算符的失败应通过抛出异常来报告。

此外,所谓的 宽合约 函数使用异常来表示不可接受的输入,例如, std::basic_string::at 没有前置条件,但会抛出异常来指示索引越界。

异常安全

当函数报告错误条件后,可能会对程序状态提供额外保证。通常认可以下四个层次的异常保证等级 [4] [5] [6] ,它们彼此之间存在严格的包含关系:

  1. Nothrow(或无失败)异常保证 — 函数绝不抛出异常。析构函数以及在栈回溯期间可能调用的其他函数应满足Nothrow(错误通过其他方式报告或隐藏)保证。 析构函数 默认标记为 noexcept (C++11 起) 交换操作、 移动构造函数 以及被强异常保证函数调用的其他函数应满足Nofail(函数始终成功)保证。
  2. 强异常保证 — 若函数抛出异常,程序状态将回滚至函数调用前的状态(例如 std::vector::push_back )。
  3. 基本异常保证 — 若函数抛出异常,程序仍处于有效状态。不会发生资源泄漏,且所有对象的不变式保持完整。
  4. 无异常保证 — 若函数抛出异常,程序可能处于无效状态:可能已发生资源泄漏、内存损坏或其他破坏不变式的错误。

泛型组件还可以提供 异常中立保证 :若从模板参数抛出异常(例如从 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 异常

外部链接

  1. H. Sutter (2004) "何时以及如何使用异常" 发表于《Dr. Dobb's期刊》
  2. H. Sutter, A. Alexandrescu (2004), 《C++编码规范》, 条款70
  3. C++核心指南 I.10:使用异常来指示未能执行必需任务的情况
  4. B. Stroustrup (2000), 《C++程序设计语言》 附录E
  5. H. Sutter (2000) 《卓越的C++》
  6. D. Abrahams (2001) "泛型组件中的异常安全性"
  7. D. Abrahams (2001) "错误与异常处理"
  8. isocpp.org 超级常见问题 "我应该抛出什么?"
  9. C++核心指南 E.14:使用专门设计的用户定义类型作为异常(而非内置类型)
  10. C++核心指南 E.15:按值抛出,通过引用捕获层次结构中的异常
  11. S. Meyers (1996) 《更有效的C++》条款13
  12. isocpp.org 超级常见问题 "我应该捕获什么?"
  13. H. Sutter, A. Alexandrescu (2004) 《C++编码规范》条款73