假设有个class用来表现夹带背景图案的GUI菜单,用于多线程,有个互斥器作为并发控制
class PrettyMenu{
public:
...
void changeBackground(std::istream& imgSrc); //改变背景
...
private:
Mutex mutex; //互斥器
Image * bgImage; //目前背景图案
int imageChanges; //背景图案改变次数
};
void PrettyMenu::changeBackground(std::istream& imgSrc){
lock(&mutex); //取得互斥器
delete bgImage; //摆脱旧背景
++imageChanges; //修改图像改变次数
bgImage = new Image(imgSrc); //安装新背景图案
unlock(&mutex); //释放互斥器
}
带有异常安全性的函数会:
1)不泄露任何资源 ,一旦new Image(imgSrc)异常,对unlock的调用就不能执行,于是互斥器永远被锁住了。
2)不允许数据败坏。如果 new Image(imgSrc)抛出异常,bgImage就指向被删除的对象,imageChanges也已被累加,其实新图像没被成功安装。
解决资源泄露很容易:
void PrettyMenu::changeBackground(std::istream& imgSrc){
Lock m1(&mutex);
delete bgImage;
++imageChanges;
bgImage=new Image(imgSrc);
}
关于数据败坏
异常安全函数提供三个保证之一:
1)基本承诺:如果异常抛出,程序内任何事物保持在有效状态
2)强烈保证:如果异常抛出,程序状态不变
3)不抛出异常
class PrettyMenu{
...
std::shared_ptr<Image>bgImage;
...
};
void PrettyMenu::changeBackground(std::istream& imgSrc){
Lock m1(&mutex);
bgImage.reset(new Image(imgSrc));
++imageChanges;
}
这两个改变几乎足够让changeBackground提供强烈的异常安全保证。如果Image构造函数抛出异常,有可能输入流读取记号已被移走,这样的搬移对程序其余部分是可见的状态改变。
copy and swap
将隶属对象的数据从源对象放进另一个对象,然后赋予源对象一个指针,指向所谓的实现对象,典型写法如下:
struct PMImple{
std::shared_ptr<Image>bgImage;
int imageChange;
};
class PrettyMenu{
...
private:
Mutex mutex;
std::shared_ptr<PMImple>pImple;
};
void PrettyMenu::changeBackground(std::istream& imgSrc){
using std::swap;
Lock m1(&mutex);
std::shared_ptr<PMImple>pnew(new PMImple(*pImple));
pnew->bgImage.reset(new Image(imgSrc));
++pNew->imageChanges;
swap(pImple,pnew);
}