c++异常安全的代码

假设有一个类,代表有背景图像的GUI菜单,这个类被设计成在多线程环境中使用,所以它有一个用于并行控制的互斥体(mutex):

class PrettyMenu {
public:
  ...
  void changeBackground(std::istream& imgSrc);           // change background
  ...                                                    // image

private:

  Mutex mutex;                    // mutex for this object

  Image *bgImage;                 // current background image
  int imageChanges;               // # of times image has been changed
};

考虑这个PrettyMenu类的changeBackground函数的可能实现:

void PrettyMenu::changeBackground(std::istream& imgSrc)
{
  lock(&mutex);                      

  delete bgImage;                    // get rid of old background
  ++imageChanges;                    // update image change count
  bgImage = new Image(imgSrc);       // install new background

  unlock(&mutex);                    // release mutex
}

如果从异常安全的观点来看,这个函数非常的烂,异常安全有两点要求,而这里全部没有满足。

当一个异常被抛出是,异常安全函数应该:

  1. 没有资源泄露。上面的代码没有通过这个测试,因为如果"new Image(imgSrc)"表达式产生一个异常,对unlock的调用就永远不会执行,而那个互斥体也将被永远挂起。
  2. 不允许数据结构恶化。如果"new Image(imgSrc)"抛出异常,bgImage被遗留下来指向一个被删除对象。另外,尽管没有将一张新的图像设置到位,imageChanges已经被增加。(在另一方面,旧的图像被明确删除,所以可能有用户看到反应之后可能会说图像已经被改变了)

规避资源泄漏问题比较容易,对这个的改进有两方面,使用c++提供的Lock机制;使用智能指针来管理新开辟的内存:

再想想,强有力的安全保证就要求如果你替换image失败,程序必须要保留被修改前一切状态,这点我们可以用swap来做,就是先保留对象最初的状态,如果新image添加成功,就替换否则放弃修改:

struct PMImpl {                               // PMImpl = "PrettyMenu
  std::tr1::shared_ptr<Image> bgImage;        // Impl."; see below for
  int imageChanges;                           // why it's a struct
};

class PrettyMenu {
  ...

private:
  Mutex mutex;
  std::tr1::shared_ptr<PMImpl> pImpl;
};

void PrettyMenu::changeBackground(std::istream& imgSrc)
{
  using std::swap;                          

  Lock ml(&mutex);                            // acquire the mutex

  std::tr1::shared_ptr<PMImpl>                // copy obj. data
    pNew(new PMImpl(*pImpl));

  pNew->bgImage.reset(new Image(imgSrc));     // modify the copy
  ++pNew->imageChanges;

  swap(pImpl, pNew);                          // swap the new
                                              // data into place

}  

在这个例子中,我选择将 PMImpl 做成一个结构体,而不是类,因为通过让 pImpl 是 private 就可以确保 PrettyMenu 数据的封装。将 PMImpl 做成一个类虽然有些不那么方便,却没有增加什么好处。如果你愿意,PMImpl 可以嵌套在 PrettyMenu 内部,像这样的打包问题与我们这里所关心的写异常安全的代码的问题没有什么关系。

所以即使当异常被抛出的时,异常安全的函数不会泄露资源,也不允许数据结构被恶意修改,而这样的函数提供基本的,强力的,或者不抛出保证。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值