C++实现之——异常安全性保证
定义类PrettyMenu如下:
1 class PrettyMenu 2 { 3 public: 4 ... 5 void changeBackground(istream& imgSrc); 6 ... 7 private: 8 Mutex mutex; 9 Image* bgImage; 10 int imageChanges; 11 };
成员函数changeBackground的实现如下:
1 void PrettyMenu::changeBackground(std::istream& imgSrc) 2 { 3 lock(&mutex); 4 delete bgImage; 5 ++ imageChanges; 6 bgImage = new Image(imgSrc); 7 unlock(&mutex); 8 }
上面的代码逻辑上是很合理的,但是如果new Image(imgSrc)的执行导致异常,那么就有可能出现两个问题:
1)mutex资源被泄漏,将被永远锁住;
2)imageChanges改变了,bgImage指向一个已删除的对象,即所谓的数据败坏了。
上面的两个问题都是异常安全性所不能存在的问题。对于资源泄漏,可以通过使用对象管理资源进行解决;而对于数据败坏, 也可以资源管理类实现,如下:
1 class PrettyMenu 2 { 3 ... 4 std::tr1::shared_ptr<Image> bgImage; 5 ... 6 }; 7 8 void PrettyMenu::changeBackground(std::istream& imgSrc) 9 { 10 Lock ml(&mutex); 11 bgImage.reset(new Image(imgSrc)); 12 13 ++ imageChanges; 14 }
这时delete语句写在了reset函数中,如果new Image有异常,那么就进不到函数reset中,最终delete语句就不会执行。
在保证要被修改的对象要么不变要么全变的一个典型策略是copy and sawp,即首先复制要改变对象的一个副本,将所有的修改都多用于该副本,修改成功之后交换两者的指针,就实现了原对象的修改,否则原对象保持不变。
通常,一个函数的最高“异常安全性保证”等价于其所调用函数中最低的那个“异常安全性保证”。
以上整理自Effective C++中文版第三版 case 29.