指导原则
不泄露任何资源
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就不会执行。死锁
unlock(&mutex);
}
上面的代码 有问题
不允许数据败坏
如果上面的代码new失败。那么bgImage就会指向一个被删除的位置。数据遭到破坏。
如何补救
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
Lock ml(&mutex); //利用14条的互斥锁对象。来自动释放。
delete bgImage;
++imageChanges;
bgImage=new Image(imgSrc);
}
三种层级的保证
基本承诺
如果异常抛出,程序内部任何事物仍然保持在 有效状态 下。
强烈保证
如果抛出异常, 程序状态 不改变。
不抛出异常
函数总能够完成功能。
君子协定
func() throw(type) //会抛出某种异常
func() throw() //不会抛出
func() throw(...) //可能是任何类型的异常
改进代码
改进1
class PrettyMenu{
std::tr1::shared_ptr<Image> bgImage; //智能指针
};
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
Lock ml(&mutex);
bgImage.reset(new Image(imgSrc));这里调用智能指针来替换
++imageChanges;
}
缺点,new
Image失败,但是可能修改输入流的读标志位。这样剩下的程序状态可能仍然受到影响。
copy and swap
构造一个副本,然后再副本身上修改。如果修改出现异常,则源对象不变。如果不出错,操作完副本之后,在替换掉换来需要赋值的变量。
struct PMImpl{
std::tr1::shared_ptr<Image> bgImage;
int imageChanges;
};
class PrettyMenu{
private:
Mutex mutex;
std::tr1::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc)
{
using std::swap;
Lock ml(&mutex);
std::tr1::shared_ptr<PMImpl>
pNew(new PMImpl(*Impl));
pNew->bgImage.reset(new Image(imgSrc));
++pNew->imageChanges;
swap(pImpl, pNew);
}
总结
如果一个系统(唯有一个)函数不具备异常安全性,真个系统就不具备异常安全性。