分享
 
 
 

翻译:Effective C++, 3rd Edition, Item 29: 争取异常安全(exception-safe)的代码(下)

王朝c/c++·作者佚名  2006-01-10
窄屏简体版  字體: |||超大  

(点击此处,接上篇)

(点击此处,访问译者 Blog http://blog.csdn.net/fatalerror99/)

无论如何,让我们把它放在一边,并且依然假装 changeBackground 可以提供强力保证。(我相信你至少能用一种方法做到这一点,或许可以通过将它的参数从一个 istream 改变到包含图像数据的文件的文件名。)有一种通常的设计策略可以有代表性地产生强力保证,而且熟悉它是非常必要的。这个策略被称为 "copy and swap"。它的原理很简单。先做出一个你要改变的对象的拷贝,然后在这个拷贝上做出全部所需的改变。如果改变过程中的某些操作抛出了异常,最初的对象保持不变。在所有的改变完全成功之后,将被改变的对象和最初的对象在一个不会抛出异常的操作中进行交换。

这通常通过下面的方法实现:将每一个对象中的全部数据从“真正的”对象中放入到一个单独的实现对象中,然后将一个指向实现对象的指针交给真正对象。这通常被称为 "pimpl idiom",Item 31 描述了它的一些细节。对于 PrettyMenu 来说,它一般就像这样:

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; // see Item 25

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

} // release the mutex

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

copy-and-swap 策略是一种全面改变或丝毫不变一个对象的状态的极好的方法,但是,在通常情况下,它不能保证全部函数都是强力异常安全的。为了弄清原因,考虑一个 changeBackground 的抽象化身—— someFunc,它使用了 copy-and-swap,但是它包含了对另外两个函数(f1 和 f2)的调用:

void someFunc()

{

... // make copy of local state

f1();

f2();

... // swap modified state into place

}

很明显,如果 f1 或 f2 低于强力异常安全,someFunc 就很难成为强力异常安全的。例如,假设 f1 仅提供基本保证。为了让 someFunc 提供强力保证,它必须写代码在调用 f1 之前测定整个程序的状态,并捕捉来自 f1 的所有异常,然后恢复到最初的状态。

即使 f1 和 f2 都是强力异常安全的,事情也好不到哪去。如果 f1 运行完成,程序的状态已经发生了毫无疑问的变化,所以如果随后 f2 抛出一个异常,即使 f2 没有改变任何东西,程序的状态也已经和调用 someFunc 时不同。

问题在于副作用。只要函数仅对局部状态起作用(例如,someFunc 仅仅影响调用它的那个对象的状态),它提供强力保证就相对容易。当函数的副作用影响了非局部数据,它就会困难得多。例如,如果调用 f1 的副作用是改变数据库,让 someFunc 成为强力异常安全就非常困难。一般情况下,没有办法撤销已经提交的数据库变化,其他数据库客户可能已经看见了数据库的新状态。

类似这样的问题会阻止你为函数提供强力保证,即使你希望去做。另一个问题是效率。copy-and-swap 的要点是这样一个想法:改变一个对象的数据的拷贝,然后在一个不会抛出异常的操作中将被改变的数据和原始数据进行交换。这就需要做出每一个要改变的对象的拷贝,这可能会用到你不能或不情愿动用的时间和空间。强力保证是非常值得的,当它可用时你应该提供它,除非在它不能 100% 可用的时候。

当它不可用时,你就必须提供基本保证。在实践中,你可能会发现你能为某些函数提供强力保证,但是效率和复杂度的成本使得它难以支持大量的其它函数。无论何时,只要你作出过一个提供强力保证的合理的成果,就没有人会因为你仅仅提供了基本保证而站在批评你的立场上。对于很多函数来说,基本保证是一个完全合理的选择。

如果你写了一个根本没有提供异常安全保证的函数,事情就不同了,因为在这一点上有罪推定是合情合理的,直到你证明自己是清白的。你应该写出异常安全的代码。除非你能做出有说服力的答辩。请再次考虑 someFunc 的实现,它调用了函数 f1 和 f2。假设 f2 根本没有提供异常安全保证,甚至没有基本保证。这就意味着如果 f2 发生一个异常,程序可能会在 f2 内部泄漏资源。这也意味着 f2 可能会恶化数据结构,例如,已排序数组可能不再排序,一个正在从一个数据结构传送到另一个数据结构去的对象可能丢失,等等。没有任何办法可以让 someFunc 能弥补这些问题。如果 someFunc 调用的函数不提供异常安全保证,someFunc 本身就不能提供任何保证。

请允许我回到怀孕。一个女性或者怀孕或者没有。局部怀孕是绝不可能的。与此相似,一个软件或者是异常安全的或者不是。没有像一个局部异常安全的系统这样的东西。一个系统即使只有一个函数不是异常安全的,那么系统作为一个整体就不是异常安全的,因为调用那个函数可能发生泄漏资源和恶化数据结构。不幸的是,很多 C++ 的遗留代码在写的时候没有留意异常安全,所以现在的很多系统都不是异常安全的。它们混合了用非异常安全(exception-unsafe)的方式书写的代码。

没有理由让事情的这种状态永远持续下去。当书写新的代码或改变现存代码时,要仔细考虑如何使它异常安全。以使用对象管理资源开始。(还是参见 Item 13。)这样可以防止资源泄漏。接下来,决定三种异常安全保证中的哪一种是你实际上能够为你写的每一个函数提供的最强的保证,只有当你不调用遗留代码就别无选择的时候,才能满足于没有保证。既是为你的函数的客户也是为了将来的维护人员,文档化你的决定。一个函数的异常安全保证是它的接口的可见部分,所以你应该特意选择它,就像你特意选择一个函数接口的其它方面。

四十年前,到处都是 goto 的代码被尊为最佳实践。现在我们为书写结构化控制流程而奋斗。二十年前,全局可访问数据被尊为最佳实践。现在我们为封装数据而奋斗,十年以前,写函数时不必考虑异常的影响被尊为最佳实践。现在我们为写异常安全的代码而奋斗。

时光在流逝。我们生活着。我们学习着。

Things to Remember

即使当异常被抛出时,异常安全的函数不会泄露资源,也不允许数据结构被恶化。这样的函数提供基本的,强力的,或者不抛出保证。强力保证经常可以通过 copy-and-swap 被实现,但是强力保证并非对所有函数都可用。一个函数通常能提供的保证不会强于他所调用的函数中最弱的保证。

 
 
 
免责声明:本文为网络用户发布,其观点仅代表作者个人观点,与本站无关,本站仅提供信息存储服务。文中陈述内容未经本站证实,其真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
2023年上半年GDP全球前十五强
 百态   2023-10-24
美众议院议长启动对拜登的弹劾调查
 百态   2023-09-13
上海、济南、武汉等多地出现不明坠落物
 探索   2023-09-06
印度或要将国名改为“巴拉特”
 百态   2023-09-06
男子为女友送行,买票不登机被捕
 百态   2023-08-20
手机地震预警功能怎么开?
 干货   2023-08-06
女子4年卖2套房花700多万做美容:不但没变美脸,面部还出现变形
 百态   2023-08-04
住户一楼被水淹 还冲来8头猪
 百态   2023-07-31
女子体内爬出大量瓜子状活虫
 百态   2023-07-25
地球连续35年收到神秘规律性信号,网友:不要回答!
 探索   2023-07-21
全球镓价格本周大涨27%
 探索   2023-07-09
钱都流向了那些不缺钱的人,苦都留给了能吃苦的人
 探索   2023-07-02
倩女手游刀客魅者强控制(强混乱强眩晕强睡眠)和对应控制抗性的关系
 百态   2020-08-20
美国5月9日最新疫情:美国确诊人数突破131万
 百态   2020-05-09
荷兰政府宣布将集体辞职
 干货   2020-04-30
倩女幽魂手游师徒任务情义春秋猜成语答案逍遥观:鹏程万里
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案神机营:射石饮羽
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案昆仑山:拔刀相助
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案天工阁:鬼斧神工
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案丝路古道:单枪匹马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:与虎谋皮
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:李代桃僵
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案镇郊荒野:指鹿为马
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:小鸟依人
 干货   2019-11-12
倩女幽魂手游师徒任务情义春秋猜成语答案金陵:千金买邻
 干货   2019-11-12
 
推荐阅读
 
 
 
>>返回首頁<<
 
靜靜地坐在廢墟上,四周的荒凉一望無際,忽然覺得,淒涼也很美
© 2005- 王朝網路 版權所有