快速业务通道

More Effective C++:防止资源泄漏

作者 佚名技术 来源 程序设计 浏览 发布时间 2012-06-30
e,const string& address,
  const string& imageFileName,
  const string& audioClipFileName)
  : theName(name), theAddress(address),
  theImage(0), theAudioClip(0)
{
 try {
  ... // 同上
}
catch (...) {
 cleanup(); // 释放资源
 throw; // 传递异常
}
}
BookEntry::~BookEntry()
{
 cleanup();
}

这就行了,但是它没有考虑到下面这种情况。假设我们略微改动一下设计,让theImage 和theAudioClip是常量(constant)指针类型:

class BookEntry {
 public:
  ... // 同上
 private:
  ...
  Image * const theImage; // 指针现在是
  AudioClip * const theAudioClip; // const类型
};

必须通过BookEntry构造函数的成员初始化表来初始化这样的指针,因为再也没有其它地方可以给const指针赋值。通常会这样初始化theImage和theAudioClip:

// 一个可能在异常抛出时导致资源泄漏的实现方法
BookEntry::BookEntry(const string& name,const string& address,const string& imageFileName,
 const string& audioClipFileName)
 : theName(name), theAddress(address),
 theImage(imageFileName != ""
 ? new Image(imageFileName)
 : 0),
 theAudioClip(audioClipFileName != ""
 ? new AudioClip(audioClipFileName)
 : 0)
 {}

这样做导致我们原先一直想避免的问题重新出现:如果theAudioClip初始化时一个异常被抛出,theImage所指的对象不会被释放。而且我们不能通过在构造函数中增加try和catch 语句来解决问题,因为try和catch是语句,而成员初始化表仅允许有表达式(这就是为什么我们必须在 theImage 和 theAudioClip的初始化中使用?:以代替if-then-else的原因)。

无论如何,在异常传递之前完成清除工作的唯一的方法就是捕获这些异常,所以如果我们不能在成员初始化表中放入try和catch语句,我们把它们移到其它地方。一种可能是在私有成员函数中,用这些函数返回指针,指向初始化过的theImage 和 theAudioClip对象。

class BookEntry {
 public:
  ... // 同上
 private:
  ... // 数据成员同上
  Image * initImage(const string& imageFileName);
  AudioClip * initAudioClip(const string&
  audioClipFileName);
};
BookEntry::BookEntry(const string& name,const string& address,const string& imageFileName,
const string& audioClipFileName): theName(name), theAddress(address),
theImage(initImage(imageFileName)),
theAudioClip(initAudioClip(audioClipFileName)){}
// theImage 被首先初始化,所以即使这个初始化失败也
// 不用担心资源泄漏,这个函数不用进行异常处理。
Image * BookEntry::initImage(const string& imageFileName)
{
 if (imageFileName != "") return new Image(imageFileName);
 else return 0;
}
// theAudioClip被第二个初始化, 所以如果在theAudioClip
// 初始化过程中抛出异常,它必须确保theImage的资源被释放。
// 因此这个函数使用try...catch 。
AudioClip * BookEntry::initAudioClip(const string& audioClipFileName)
{
 try {
  if (audioClipFileName != "") {
   return new AudioClip(audioClipFileName);
  }
  else return 0;
 }
 catch (...) {
  delete theImage;
 throw;
 }
}

上面的程序的确不错,也解决了令我们头疼不已的问题。不过也有缺点,在原则上应该属于构造函数的代码却分散在几个函数里,这令我们很难维护。

更好的解决方法是采用条款9的建议,把theImage 和 theAudioClip指向的对象做为一个资源,被一些局部对象管理。这个解决方法建立在这样一个

凌众科技专业提供服务器租用、服务器托管、企业邮局、虚拟主机等服务,公司网站:http://www.lingzhong.cn 为了给广大客户了解更多的技术信息,本技术文章收集来源于网络,凌众科技尊重文章作者的版权,如果有涉及你的版权有必要删除你的文章,请和我们联系。以上信息与文章正文是不可分割的一部分,如果您要转载本文章,请保留以上信息,谢谢!

分享到: 更多

Copyright ©1999-2011 厦门凌众科技有限公司 厦门优通互联科技开发有限公司 All rights reserved

地址(ADD):厦门软件园二期望海路63号701E(东南融通旁) 邮编(ZIP):361008

电话:0592-5908028 传真:0592-5908039 咨询信箱:web@lingzhong.cn 咨询OICQ:173723134

《中华人民共和国增值电信业务经营许可证》闽B2-20100024  ICP备案:闽ICP备05037997号