item29: 为“异常安全”而努力是值得的

来源:互联网 发布:如何当一名网络写手 编辑:程序博客网 时间:2024/04/30 09:58

我得承认,本条款对异常安全的要求简直有点吹毛求疵,但是当你在攻克C++的路上越走越远的时候,对自己严格一点要求有什么不好呢。

让我们先从一份简单的代码说起:

class PrettyMenu{public:void changeBackground(std::istream& imgSrc);private:Mutex mutex;Image* bgImage;int imageChange;...};void PrettyMenu::changeBackground(std::istream& imgSrc){lock(&mutex);delete bgImage;++imageChange;bgImage = new Image(imgSrc);unlock(&mutex);}

看起来是份很寻常的代码,但是我得说它的问题可真不少。让我们假设new Image的时候发生异常(这是很有可能的,比如内在不足就会抛出bad_allock),结果mutex就永远被锁住了,同时bgImage没有得到正常的更新,而imageChange数据却更新了。

在这里,我要定义一下关于异常安全的三种保证,即你的代码得符合以下三种标准之一才可称得上是异常安全的:

  • 基本承诺 ,即在异常抛出后所有数据都不被破坏,以上面的代码为例,我们可以在抛出异常后增加新的代码使得bgImage回到原来的数据或者干脆直接将它指向一个默认的数据
  • 强烈保证,若有异常抛出,所有数据都保持原样
  • 不抛异常
对于上述三种保证,先说不抛异常吧,除非你只使用C++最简单的那一部分,不然你真的很难控制你的代码绝不异常(刚刚说的内存不足并没有你想像中那么少见)。所以我们可以做的往往是争取让代码处于另两种异常安全标准之中。
让我们往前走一步,我来提供一个达到基本承认的异常标准代码:
class PrettyMenu{public:void changeBackground(std::istream& imgSrc);private:Mutex mutex;shared_ptr<Image> bgImage;int imageChange;...};void PrettyMenu::changeBackground(std::istream& imgSrc){Lock(&mutex); //see item14bgImage.reset(new Image(imgSrc));++imageChange;}

看起来似乎够好了,但是如果我更严格一点,要求一个强烈保证异常安全的版本呢。这个时候我们不得不使用copy and swap的策略了。
struct PMImpl{shared_ptr<Image> bgImage;int imageChange;};class PrettyMenu{public:void changeBackground(std::istream& imgSrc);private:Mutex mutex;shared_ptr<PMImpl> pImpl;...};void PrettyMenu::changeBackground(std::istream& imgSrc){using std::swap;Lock(&mutex); //see item14shared_ptr<PMImpl> pNew(new PMImpl(imgSrc));++pNew.imageChange;swap(pImpl, pNew);}

是不是要长吸一口气,不过当你开始真正追求异常安全的代码之后,你可能就会对这类的代码感到习以为常了。

0 0