当前位置: 首页 > news >正文

网站 制作营销培训班

网站 制作,营销培训班,中国建设银行蚌埠官方网站,温州app软件开发如果你正在开发一个具有多媒体功能的通讯录程序。这个通讯录除了能存储通常的文字信息如姓名、地址、电话号码外,还能存储照片和声音。 可以这样设计: class Image // 用于图像数据 { public: Image(const string& imageDataFileName); ... }; c…

如果你正在开发一个具有多媒体功能的通讯录程序。这个通讯录除了能存储通常的文字信息如姓名、地址、电话号码外,还能存储照片和声音。
可以这样设计:

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<PhoneNumber> thePhones; // 他的电话号码 Image *theImage; // 他们的图像 AudioClip *theAudioClip; // 他们的一段声音片段 
};

编写 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; 
}

构造函数把指针 theImagetheAudioClip 初始化为空,然后如果其对应的构造函数参数不是空,就让这些指针指向真实的对象。

析构函数负责删除这些指针,确保 BookEntry对象不会发生资源泄漏。因为 C++确保删除空指针是安全的,所以BookEntry 的析构函数在删除指针前不需要检测这些指针是否指向了某些对象。

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

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

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

假设建立 theAudioClip 对象建立时,一个异常被抛出,那么谁来负责删除 theImage 已经指向的对象呢?答案显然应该是由 BookEntry 来做,但是这个想当然的答案是错的。~BookEntry()根本不会被调用,永远不会。

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

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

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

void testBookEntryClass() 
{ BookEntry *pb = nullptr; 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 进行赋值操作。

C++拒绝为没有完成构造操作的对象调用析构函数是有一些原因的,而不是故意为你制造困难。
原因是:在很多情况下这么做是没有意义的,甚至是有害的。如果为没有完成构造操作的对象调用析构函数,析构函数如何去做呢?仅有的办法是在每个对象里加入一些字节
来指示构造函数执行了多少步?然后让析构函数检测这些字节并判断该执行哪些操作。这样的记录会减慢析构函数的运行速度,并使得对象的尺寸变大。C++避免了这种开销,但是代价是不能自动地删除被部分构造的对象

因为当对象在构造中抛出异常后 C++不负责清除对象,所以你必须重新设计你的构造函数以让它们自己清除。经常用的方法是捕获所有的异常,然后执行一些清除代码,最后再重新抛出异常让它继续转递。如下所示,在BookEntry 构造函数中使用这个方法:

BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName) : theName(name), theAddress(address), theImage(0), theAudioClip(0) 
{ try { if (imageFileName != "") { theImage = new Image(imageFileName); } if (audioClipFileName != "") { theAudioClip = new AudioClip(audioClipFileName); } } catch (...) // 捕获所有异常 { delete theImage; // 完成必要的清除代码 delete theAudioClip; throw; // 继续传递异常 } 
}

你可能已经注意到 BookEntry 构造函数的 catch 块中的语句与在 BookEntry 的析构函数的语句几乎一样。这里的代码重复是绝对不可容忍的,所以最好的方法是把通用代码移入一个私有 helper function 中,让构造函数与析构函数都调用它。

class BookEntry 
{ 
public: ... // 同上 
private: ... void cleanup(); // 通用清除代码 
}; 
void BookEntry::cleanup() 
{ delete theImage; delete theAudioClip; 
} 
BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName) : theName(name), theAddress(address), theImage(0), theAudioClip(0) 
{ try { ... // 同上 } catch (...) { cleanup(); // 释放资源 throw; // 传递异常 } 
} 
BookEntry::~BookEntry() 
{ cleanup(); 
}

这似乎行了,但是它没有考虑到下面这种情况。假设我们略微改动一下设计,让theImagetheAudioClip 是常量指针类型:

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

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

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 所指的对象不会被释放。而且我们不能通过在构造函数中增加 trycatch 语句来解决问题,因为 trycatch 是语句,而成员初始化表仅允许有表达式(这也是为什么我们必须在 theImagetheAudioClip 的初始化中使用?:以代替 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 nullptr; 
} 
// theAudioClip 被第二个初始化, 所以如果在 theAudioClip 
// 初始化过程中抛出异常,它必须确保 theImage 的资源被释放。 
// 因此这个函数使用 try...catch 。 
AudioClip * BookEntry::initAudioClip(const string& 
audioClipFileName) 
{ try { if (audioClipFileName != "") { return new AudioClip(audioClipFileName); } else return nullptr; } catch (...) { delete theImage; throw; } 
}

上面的程序的确不错,也解决了令我们头疼不已的问题。不过也有缺点,在原则上应该属于构造函数的代码却分散在几个函数里,这令我们很难维护.
更好的解决方法是采用条款 09的建议,theImagetheAudioClip 指向的对象做为一个资源,被一些局部对象管理。

class BookEntry 
{ 
public: ... // 同上 
private: ... const unique_ptr<Image> theImage; // 它们现在是 const unique_ptr<AudioClip> theAudioClip; // unique_ptr 对象 
};

这样做使得 BookEntry 的构造函数即使在存在异常的情况下也能做到不泄漏资源,而且让我们能够使用成员初始化表来初始化 theImagetheAudioClip,如下所示:

BookEntry::BookEntry(const string& name, const string& address, const string& imageFileName, const string& audioClipFileName) : theName(name), theAddress(address), theImage(imageFileName != "" ? new Image(imageFileName) : nullptr), theAudioClip(audioClipFileName != "" ? new AudioClip(audioClipFileName) : nullptr){}

在这里,如果在初始化 theAudioClip 时抛出异常,theImage 已经是一个被完全构造的对象,所以它能被自动删除掉,就象 theName, theAddressthePhones 一样。而且因为theImagetheAudioClip 现在是包含在 BookEntry 中的对象,当 BookEntry 被删除时它们能被自动地删除。因此不需要手工删除它们所指向的对象。可以这样简化 BookEntry 的析构函数:

BookEntry::~BookEntry(){}

总结

在对象构造中,处理各种抛出异常的可能,是一个棘手的问题,但是智能指针能化繁为简。它不仅把令人不好理解的代码隐藏起来,而且使得程序在面对异常的情况下也能保持正常运行。

http://www.lbrq.cn/news/2618803.html

相关文章:

  • 成都网站建设电话app注册推广拉人
  • 专业团队图片素材美国seo薪酬
  • win2008sr怎么用iis做网站电子商务网站开发
  • 哪个公司做网站好徐州seo建站
  • 长沙建设局网站如何制作自己的网站
  • 网站如何做淘宝支付微信广告推广平台
  • 浙江省住房和城乡建设厅网站网红营销
  • 百度小程序开发者平台怎么做seo信息优化
  • 衡水做网站服务商网络营销课程作业
  • 有公网ip 建网站关键词优化公司
  • 网站源码怎么做网站网络营销seo优化
  • 网站建设的技术有哪些方面关键词挖掘工具网站
  • 学院 网站 两学一做谷歌外贸
  • 山西省城乡住房建设厅网站百度知道登录入口
  • 网站建设维护一年费用百度云网盘资源
  • 织梦批量修改网站源代码seo点击排名源码
  • 东方城乡与住房建设部网站seo招聘信息
  • 郑州市网站和公众号建设百度站长工具seo
  • 中国商务部市场建设司网站关键词的优化方法
  • 大学生做微商网站温州网站建设开发
  • 网站模板分什么类型网站推广计划书范文
  • 房产集团网站建设app推广赚钱平台
  • 佛山专业网站建设公司seo策略什么意思
  • 宽带费用多少钱一年湛江seo网站管理
  • 做网站排名费用seo学徒招聘
  • 做微网站的公司百度指数如何分析
  • 平台网站建设所需资质灰色行业推广
  • 濮阳房产网站建设保定seo网络推广
  • 网站如何做快捷支付长春网站开发
  • 网站制作公司服务产品网络营销策划方案
  • iOS 文件管理实战指南,用户文件、安全访问与开发调试方案
  • MySQL梳理四:事务日志机制和多版本并发控制(MVCC)
  • 物联网架构全解析:华为“1+2+1”与格行随身WiFi,技术如何定义未来生活?
  • 多端同步新解法:Joplin+cpolar联合通过开源设计实现跨平台无缝协作?
  • AI 软件工程开发 AI 算法 架构与业务
  • 【运维部署篇】OpenShift:企业级容器应用平台全面解析