命名空间
变体
操作

异常

来自 cppreference.cn
< cpp‎ | 语言
 
 
C++ 语言
通用主题
流程控制
条件执行语句
if
迭代语句 (循环)
for
范围-for (C++11)
跳转语句
函数
函数声明
Lambda 函数表达式
inline 说明符
动态异常规范 (在 C++17 前已弃用*)
noexcept 说明符 (C++11)
异常
命名空间
类型
说明符
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)
 

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

计算 throw 表达式 将会抛出一个异常。异常也可能在其他上下文中抛出。

为了捕获异常,throw 表达式必须位于 try 内,并且 try 块必须包含一个与异常对象的类型匹配的 处理程序

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

(在 C++17 前)
(自 C++11 起)

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

目录

[编辑] 用法

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

[编辑] 错误处理

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

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

特别是,这意味着构造函数(另请参见 RAII)和大多数运算符的失败应通过抛出异常来报告。

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

[编辑] 异常安全性

在函数报告错误条件后,可以提供关于程序状态的附加保证。以下是通常公认的四个级别的异常保证[4][5][6],它们是彼此的严格超集

  1. 不抛出(或无失败)异常保证 — 函数从不抛出异常。不抛出(错误通过其他方式报告或隐藏)是 析构函数 和可能在堆栈展开期间调用的其他函数所期望的。析构函数 默认是 noexcept 的。(自 C++11 起) 无失败(函数总是成功)是交换、移动构造函数 以及那些提供强异常保证的函数所使用的其他函数所期望的。
  2. 强异常保证 — 如果函数抛出异常,程序的状态将回滚到函数调用之前的状态(例如,std::vector::push_back)。
  3. 基本异常保证 — 如果函数抛出异常,程序处于有效状态。没有资源泄漏,并且所有对象的不变式都完好无损。
  4. 无异常保证 — 如果函数抛出异常,程序可能不处于有效状态:可能发生资源泄漏、内存损坏或其他破坏不变式的错误。

泛型组件可能还会提供异常中性保证:如果从模板参数(例如,从 std::sortCompare 函数对象或 std::make_sharedT 的构造函数)抛出异常,则会将其不变地传播给调用者。

[编辑] 异常对象

虽然任何完整类型的对象和指向 void 的 cv 指针都可以作为异常对象抛出,但所有标准库函数都按值抛出未命名的对象,并且这些对象的类型(直接或间接地)派生自 std::exception。用户定义的异常通常遵循此模式。[7][8][9]

为了避免不必要的异常对象复制和对象切片,处理程序的最佳实践是通过引用捕获。[10][11][12][13]

[编辑] 注解

特性测试宏 Std 特性
__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) “Exceptional C++”
  6. D. Abrahams (2001) “泛型组件中的异常安全性”
  7. D. Abrahams (2001) “错误和异常处理”
  8. isocpp.org 超级 FAQ “我应该抛出什么?”
  9. C++ 核心指南 E.14: 使用专门设计的用户定义类型作为异常(而不是内置类型)
  10. C++ 核心指南 E.15: 按值抛出,按引用捕获来自层次结构的异常
  11. S. Meyers (1996) “More Effective C++” 第 13 项
  12. isocpp.org 超级 FAQ “我应该捕获什么?”
  13. H. Sutter, A. Alexandrescu (2004) “C++ 编码标准” 第 73 项