Effective C++:条款29:为“异常安全”而努力是值得的

(一)先看下面这些代码:

class PrettyMenu {
public:
	void changeBackground(istream& imgSrc);
private:
	Mutex mutex;          //由于这个class希望用于多线程环境,所以它有这个互斥器作为并发控制之用
	Image* bgImage;       //目前的背景图像
	int imageChanges;     //背景图像被改变的次数
};

void PrettyMenu::changeBackground(std::istream &imgSrc) {
	lock(&mutex);                    //取得互斥器
	delete bgImage;                  //摆脱旧的背景图像
	++imageChanges;                  //修改图像变更次数
	bgImage = new Image(imgSrc);     //安装新的背景图像
	unlock(&mutex);                  //释放互斥器
}

上面的函数不是“异常安全性”函数,因为“异常安全性”函数所需满足的两个条件,上面这个函数都不满足。。

第一个条件:不泄露任何资源。(上面这个函数当new Image(imgSrc)发生异常的话,unlock(&mutex)就永远不会被执行,所以就资源泄漏了)。

第二个条件:不允许数据败坏。(上面这个函数当new Image(imgSrc)发生异常的话,bgImage就是指向一个已删除的对象)。

(二)

(1)解决上面那个函数泄漏资源的问题的办法:用Lock class作为一种“确保互斥器被及时释放”的方法,以对象管理资源:

void PrettyMenu::changeBackground(std::istream &imgSrc) {
	Lock m1(&mutex);
	delete bgImage;                  //摆脱旧的背景图像
	++imageChanges;                  //修改图像变更次数
	bgImage = new Image(imgSrc);     //安装新的背景图像
}

(2)解决上面那个函数数据败坏的办法:

首先,

异常安全函数提供以下三个保证之一:

1.  基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效的状态下。

2.  强烈保证:如果异常被抛出,程序状态不改变。

3.  不抛掷保证:承诺绝不抛出异常,因为它们总是能够完成它们原先承诺的功能。

对changeBackground而言,首先,从一个类型为Image*的内置指针改为一个“用于资源管理”的智能指针,第二,重新排列changeBackground内的语句次序,使得在更换图像之后再累加imageChanges。重新排列语句次序

class PrettyMenu {
    ...
    std::tr1::shared_ptr<Image> bgImage;
    ...
};

void PrettyMenu::changeBackground(std::istream& imgSrc) {
    Lock ml(&mutex);
    bgImage.reset(new Image(imgSrc));
    ++imageChanges;
}

这两个改变几乎足够让changeBackground提供强烈的异常安全保证。美中不足的是参数imgSrc。如果Image构造函数抛出异常,有可能输入流的读取记号已被移走,而这样的搬移对程序其余部分是一种可见的状态改变。

(三)

copy and swap的原则:为打算修改的对象做一个副本,在那个副本上做一切必要修改。若有任何修改动作抛出异常,源对象仍然保持未改变状态。待所有改变都成功后,再将修改过的副本和原对象在一个不抛出异常的swap中置换。

实际上通常是将所有“隶属对象的数据”从原对象放进另一个对象内,然后赋予原对象一个指针,指向那个所谓的实现对象(即副本)。

实现如下:使用copy
and swap方法下的pimpl idiom手段:

struct PMImpl {
    std::tr1::shared_ptr<Image> bgImage;
    int imageChanges;
};
class PrettyMenu {
private:
    Mutex mutex;
    std::tr1::shared_ptr<PMImpl> pImpl;
};
void PrettyMenu::changeBackground(std::istream& imgSrc) {
    using std::swap;
    Lock ml(&mutex);
    std::tr1::shared_ptr<PMImpl> pNew(new PMImpl(*pImpl));
    pNew->bgImage.reset(new Image(imgSrc)); //修改副本
    ++pNew->imageChanges;
    swap(pImpl, pNew);//置换数据
}

请记住:

(1)异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏。这样的函数区分为三种可能的保证:基本型、强烈型、不抛异常型。

(2)“强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义。

(3)函数提供的“异常安全保证”通常最高只等于其所调用的各个函数的“异常安全保证”中的最弱者。

Effective C++:条款29:为“异常安全”而努力是值得的

时间: 2024-11-08 17:28:47

Effective C++:条款29:为“异常安全”而努力是值得的的相关文章

Effective C++ Item 29 为”异常安全”而努力是值得的

本文为senlie原创,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏.这样的函数区分为三种 可能的保证: 基本型-->发生异常,程序处于某个合法状态 强烈型-->发生异常,程序处于原先状态 不抛异常型-->承诺绝不抛出殿堂 示例: class PrettyMenu{ public: //... void changeBackground(std::istream &imgSr

Effective C++ -----条款29:为“异常安全”而努力是值得的

异常安全函数(Exception-safe functions)即使发生异常也不会泄露资源或允许任何数据结构败坏.这样的函数区分为三种可能的保证:基本型.强烈型.不抛异常型. “强烈保证”往往能够以copy-and-swap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义. 函数提供的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者.

More Effective C++ 条款11 禁止异常流出destructor之外

1. ”两种情况下destructor会被调用.第一种情况是当对象在正常情况下被销毁,也就是当它离开了它的生存空间或是被明确的删除:第二种情况是当对象被exception处理机制——也就是exception传播过程中的stack-unwinding(栈展开)机制——销毁.” 2. 当destructor被调用时,可能(也可能不)有一个exception正在作用之中,但我们无法再destructor中区分这些状态(现在有了区分的办法.1995年7月 IOS/ANSI C++ 标准委员会加入一个新函

条款29:为“异常安全”而努力是值得的

资源.数据.状态 http://blog.csdn.net/u013540854/article/details/30721675 结论1:异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏.这样的函数区分为三种可能的保证:基本型.强烈型.不抛异常型. "异常安全"有两个条件:不泄漏任何资源和不允许数据败坏.解决资源泄漏问题可用对象管理资源.对于数据败坏,异常安全函数提供三个保证: 基本承诺:如果异常被抛出,程序内的任何事物仍然保持在有效状态下.没有任何对象或数据结构会因此而

[Effective C++ --029]为“异常安全”而努力是值得的

假设有个class用来表现夹带背景图案的GUI菜单单,这个class用于多线程环境,所以它有个互斥器(mutex)作为并发控制用: 1 class PrettyMenu{ 2 public: 3 ... 4 void changeBackground(std::istream& imgSrc); 5 ... 6 private: 7 Mutex mutex; 8 Image* bgImage; 9 int imageChanges; 10 }; 11 void PrettyMenu::chang

Effective C++(Third Edition) Item29 为“异常安全”而努力是值得的

“异常安全”有两个条件: 1.不泄露任何资源 可以通过以对象管理资源的方式(Item13). 2.不允许数据败坏 异常安全函数提供以下三种保证之一 a.基本承诺 如果异常被抛出,程序内的任何事物都仍然保持在有效状态下,但是何种状态未知. b.强烈保证 如果异常被抛出,程序状态不改变. c .不抛掷(nothrow)保证 注意:不要为了表示某件事情发生而改变对象状态,除非那件事情真的发生了. copy and swap策略. 总结:异常安全函数即使发生异常也不会泄漏资源或允许任何数据结构败坏,这样

《Effective C++》学习笔记——条款29

***************************************转载请注明出处:http://blog.csdn.net/lttree******************************************** 五.Implementations Rule 29:Strive for exception-safe code 规则 29:为"异常安全"而努力是值得的 一.一个例子来引发这个规则 假设有个 class 用来表现夹带背景图案的GUI菜单.这个 cla

effective c++条款26-31“class and function的实现”整理

一.类的实现面临的问题: 太快定义变量可能造成效率上的拖延:过度使用转型(casts)可能导致代码变慢又难维护,又招来微妙难解的错误:返回对象"内部数据之号码牌(handls)"可能会破坏封装并留给客户虚吊号码牌:为考虑异常带来的冲击则可能导致资源泄漏和数据败坏:过度热心地inlining可能引起代码膨胀:过度耦合则可能导致让人不满意的冗长建置时间. 二.条款26:尽可能延后变量定义式的出现时间 有些对象,你可能过早的定义它,而在代码执行的过程中发生了导常,造成了开始定义的对象并没有被

Effective C++ 条款八 别让异常逃离析构函数

class DBConn //这个class用来管理DBConnction对象 { public:   //自己设计一个新的DBConn接口 方法3 void close() { db.close(); closed = true; }     ~DBConn() //确保数据库连接总是会被关闭 { //db.close();   if (!closed) { try { db.close(); } catch() { //制作运转记录,记下对close的调用失 } } } protected: