Effective C++ 条款29 为"异常安全"而努力是值得的
1. 当异常被抛出时,"异常安全"函数有两个条件:
不泄露任何资源:从堆中申请的资源应该确保被释放
不允许数据败坏:函数不能对数据修改到一半而抛出异常以致数据被破坏.
解决"不泄露任何资源"很容易,只要使用资源管理类(如shared_ptr,见条款13)即可,"不允许数据败坏"是主要考虑的问题.
2. 异常安全函数提供以下三种层次的保证:
1) 基本保证:如果异常被抛出,"程序内任何事物仍然保持在有效状态下.没有任何对象或数据结构会因此而败坏."然而程序的现实状态很难预料:客户必须调用成员函数来确认对象的状态.
2) 强烈保证:如果异常被抛出,程序状态不改变."如果函数成功,就是完全成功,如果函数失败,程序会回复到'调用函数之前'的状态".
3) 不抛掷(nothrow)保证:程序绝不抛出异常且总是能够完成承诺的内容.
以上三种层次的保证逐渐增强.
3. 一般来说,实现不抛掷保证不太现实:1) 如果程序要保证nothrow,那么就要保证它所调用的函数也nothrow.2)任何使用动态内存的代码(如STL容器)都有可能抛出无法找到足够内存而产生的ban_alloc异常,因此,提供异常安全保证通常从基本保证和强烈保证中选择.
要实现1中所提出的两个条件,一般有以下策略:
1) 资源管理的使用以确保不泄露堆中资源.
2) 对函数语句顺序的细致规划以阻止数据的败坏,这未必能完全避免数据败坏.
3) 使用"copy and swap"策略:为打算修改的对象做出一份副本,然后在副本上进行修改,若函数抛出异常,只有副本的数据发生败坏,若修改成功执行,调用swap函数进行置换.这是解决数据败坏的有效途径.
注意:途径3)虽然有效,但是仍然有以下限制:
1)使用"copy and swap"策略构造临时对象,因此要付出额外的资源和效率负担.
2)要使用swap函数,必须保证swap函数不抛出任何异常(见条款25).
3)使用"copy and swap"策略并不能彻底根除数据败坏的可能性,如果函数内调用其它函数,会产生"连带影响".
4. 异常安全符合短板原理:一个软件系统内只要有一个函数不符合异常安全性,整个软件系统就不具备异常安全性.