异常
异常处理提供了一种方法,用于将控制权和信息从程序执行中的某个点传递到与执行先前经过的点关联的处理程序(换句话说,异常处理将控制权向上转移到调用堆栈)。
计算 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]
[编辑] 注解
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__cpp_constexpr_exceptions |
202411L |
(C++26) | constexpr 异常 |
[编辑] 外部链接
|