软件频道>程序开发>JavaVBVCDelphiC/C++Web开发微软专栏移动数据库程序人生软件工程|开发客
您现在的位置: 天极网 > 开发频道 > More Effective C++:防止资源泄漏
全文

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

2006-09-28 00:07作者:出处:BLOG责任编辑:方舟
  如果你正在开发一个具有多媒体功能的通讯录程序。这个通讯录除了能存储通常的文字信息如姓名、地址、电话号码外,还能存储照片和声音(可以给出他们名字的正确发音)。

  为了实现这个通信录,你可以这样设计:

class Image { // 用于图像数据
 public:
  Image(const string& imageDataFileName);
  ...
};

class AudioClip { // 用于声音数据
 public:
  AudioClip(const string& audioDataFileName);
  ...
};

class PhoneNumber { ... }; // 用于存储电话号码
class BookEntry { // 通讯录中的条目
 public:
  BookEntry(const string& name,
  const string& address = "",
  const string& imageFileName = "",
  const string& audioClipFileName = "");
  ~BookEntry();
  // 通过这个函数加入电话号码
  void addPhoneNumber(const PhoneNumber& number);
  ...
 private:
  string theName; // 人的姓名
  string theAddress; // 他们的地址
  list thePhones; // 他的电话号码
  Image *theImage; // 他们的图像
  AudioClip *theAudioClip; // 他们的一段声音片段
};

  通讯录的每个条目都有姓名数据,所以你需要带有参数的构造函数(参见条款3),不过其它内容(地址、图像和声音的文件名)都是可选的。注意应该使用链表类(list)存储电话号码,这个类是标准C++类库(STL)中的一个容器类(container classes)。(参见Effective C++条款49 和本书条款35)

  编写BookEntry 构造函数和析构函数,有一个简单的方法是:

BookEntry::BookEntry(const string& name,const string& address,
 const string& imageFileName,
 Const string& audioClipFileName)
 : theName(name), theAddress(address),
 theImage(0), theAudioClip(0)
 {
  if (imageFileName != "") {
   theImage = new Image(imageFileName);
  }
  if (audioClipFileName != "") {
   theAudioClip = new AudioClip(audioClipFileName);
  }
 }
 BookEntry::~BookEntry()
 {
  delete theImage;
  delete theAudioClip;
 }

  构造函数把指针theImage和theAudioClip初始化为空,然后如果其对应的构造函数参数不是空,就让这些指针指向真实的对象。析构函数负责删除这些指针,确保BookEntry对象不会发生资源泄漏。因为C++确保删除空指针是安全的,所以BookEntry的析构函数在删除指针前不需要检测这些指针是否指向了某些对象。

  看上去好像一切良好,在正常情况下确实不错,但是在非正常情况下(例如在有异常发生的情况下)它们恐怕就不会良好了。

  请想一下如果BookEntry的构造函数正在执行中,一个异常被抛出,会发生什么情况呢?:

if (audioClipFileName != "") {
 theAudioClip = new AudioClip(audioClipFileName);
}

  一个异常被抛出,可以是因为operator new(参见条款8)不能给AudioClip分配足够的内存,也可以因为AudioClip的构造函数自己抛出一个异常。不论什么原因,如果在BookEntry构造函数内抛出异常,这个异常将传递到建立BookEntry对象的地方(在构造函数体的外面。 译者注)。

  现在假设建立theAudioClip对象建立时,一个异常被抛出(而且传递程序控制权到BookEntry构造函数的外面),那么谁来负责删除theImage已经指向的对象呢?答案显然应该是由BookEntry来做,但是这个想当然的答案是错的。BookEntry根本不会被调用,永远不会。

  C++仅仅能删除被完全构造的对象(fully contructed objects), 只有一个对象的构造函数完全运行完毕,这个对象才能被完全地构造。所以如果一个BookEntry对象b做为局部对象建立,如下:

void testBookEntryClass()
{
 BookEntry b("Addison-Wesley Publishing Company","One Jacob Way, Reading, MA 01867");
 ...
}

  并且在构造b的过程中,一个异常被抛出,b的析构函数不会被调用。而且如果你试图采取主动手段处理异常情况,即当异常发生时调用delete,如下所示:

void testBookEntryClass()
{
 BookEntry *pb = 0;
 try {
  pb = new BookEntry("Addison-Wesley Publishing Company","One Jacob Way, Reading, MA 01867");
  ...
 }
 catch (...) { // 捕获所有异常
  delete pb; // 删除pb,当抛出异常时
 throw; // 传递异常给调用者
}

delete pb; // 正常删除pb
}

  你会发现在BookEntry构造函数里为Image分配的内存仍旧被丢失了,这是因为如果new操作没有成功完成,程序不会对pb进行赋值操作。如果BookEntry的构造函数抛出一个异常,pb将是一个空值,所以在catch块中删除它除了让你自己感觉良好以外没有任何作用。用灵巧指针(smart pointer)类auto_ptr(参见条款9)代替raw BookEntry*也不会也什么作用,因为new操作成功完成前,也没有对pb进行赋值操作。

软件资讯·软件下载尽在天极软件

共2页。 1 2 下一页 末页
相关搜索:
关注此文读者还看过
热门关注
特别推荐
文章排行
本周
本月
最近更新
关于我们|About us|网站律师|天极服务|电子杂志|RSS订阅|加入我们|网站地图
TMG
Copyright (C) 1999-2009 Chinabyte.com, All Rights Reserved 版权所有 天极网络
商务联系、网站内容、合作建议:010-82657868
版权声明 在线提交意见反馈 渝ICP证B2-20030003号
经营性网站备案信息 网警备案 中国网站排名
天极传媒:天极网|比特网|IT专家网|IT商网|52PK游戏网|IT分众