第11章

  1. 一个具备错误安全性的程序,即使遇到错误,也能保持一个定义良好的状态(一组不变量)。异常安全是错误安全性的一种特定形式,假设错误通过抛出表达式来表示。当抛出(允许的)表达式时,程序不能进入未定义状态。一个异常安全的程序可能要求某些操作不抛出异常。
  2. 如果必须在多个可能失败的操作之间保持一致的状态,那么当后续操作失败时,之前的操作必须被撤销。这通常要求这些操作在事务成功结束之前不完全提交。最终的提交操作必须不会失败(例如,抛出异常),否则无法保证错误安全性。回滚操作也必须不会失败。
  3. RAII 类确保当程序离开某个作用域(例如函数)时,一定会执行某个操作。使用 RAII,即使函数通过提前返回,或抛出异常而提前退出作用域,关闭操作也不会跳过或绕过。
  4. 经典的 RAII 需要为每个操作专门编写一个类。而 ScopeGuard 可以从代码段(至少在支持 Lambda 表达式的情况下)自动生成一个 RAII 类。
  5. 如果状态通过错误码返回,则无法实现。如果程序中的所有错误都通过异常表示,并且函数的返回都代表成功,那么可以在运行时检测是否抛出了异常。复杂之处在于,保护操作本身可能发生在由另一个异常引起的栈展开过程中。当保护类需要判断操作是否成功时,该异常正在传播,但其存在并不表示保护操作的失败(可能表示其他地方的其他操作失败了)。健壮的异常检测必须跟踪在保护作用域开始和结束时正在传播的异常数量,这只有在 C++17(或使用编译器扩展)中才可能实现。
  6. ScopeGuard 类通常是模板实例化。开发者通常不知道 ScopeGuard 的具体类型,或者至少难以显式指定。ScopeGuard 依赖于生命周期延长和模板参数推导来管理这种复杂性。类型擦除的 ScopeGuard 是一个具体类型,不依赖于其所持有的代码。缺点是类型擦除需要运行时多态性,并且大多数情况下需要内存分配。