异常安全函数:即使发生异常也不会泄露资源或允许任何数据结构败坏。
异常安全函数需要提供以下三个保证之一:
-
基本承诺:
如果异常被抛出,程序内的任何事物任然保持在有效状态下。没有任何对象或数据结构会因此而败坏,所有对象都处于一种内部前后一致的状态(如所有的class约束条件都继续获得满足)。然而程序的现实状态恐怕不可预料。 -
强烈保证:
如果异常被抛出,程序状态不改变。即
函数成功,就是完全成功;
函数失败,程序会回复到“调用之前”的状态;
与“基本承诺”的对比:
基本承诺:有可能处于任何状态——只要该状态合法;
强烈保证:只有两种状态;
- 不抛掷(no throw)保证。
注意:函数声明式(包括其异常明细——如果有的话)并不能告诉你是否它是正确的、可移植的或高效得,也不能告诉你它是否提供任何异常安全性保证。所有那些性质都由函数的实现决定,无关乎声明。
一个强烈保证的一般化设计策略——copy and swap 策略
实现上通常是将所有“隶属对象的数据”从原对象放进另一个对象内,然后赋予原对象一个指针,指向那个所谓的实现对象(即副本)。
struct PMImpl{
shared_ptr<Image> bgImage;
int imageChanges;
};
class PrettyMenu{
...
private:
Mutex mutex;
shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
using std::swap;
Lock m1(&mutex);
shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
pNew->bgImage.reset(new Image(imgSrc));
++pNew->imageChanges;
swap(pImpl, pNew);
}
copy and swap 并不保证整个函数有强烈的异常安全性,如果函数里面还有其他函数调用(它们并无异常安全性或修改了非局部性数据)
- 函数提供的异常安全保证通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。
- 强烈保证往往能够以copy-and-swap实现出来,但并非对所有函数都有课实现或具备现实意义(copy and swap 耗费资源通常都耗费资源)