Effective C++ 第二十九条 Strive for exception-safe code.|环球报资讯
为“异常安全”而努力是值得的
我们在书写函数的时候难免会碰到异常,但是碰到异常我们应当保证两点:
不泄漏任何资源
(资料图)
不允许数据破坏
下面有一段在安全性上非常糟糕的代码
在这段代码中,一旦 new Image (imgSrc) 出现异常,后续的 unlock 操作就不会执行,于是互斥器就永远锁住了。而且原有的 bgImage 被删除,imageChanges 也被改变,然而没有新的图像被替换。
一般来说,代码越少就越好,因为语句越少,出错的可能就越少。,一旦有一句改变,被误解的机会也少。
我们在 第十三条 说过,使用资源管理类来管理资源可以避免资源泄漏这类事情发生,于是代码变成了这样
但是数据已经可能被破坏,这一改变只解决了资源泄漏,没有解决数据破坏。
在《Effective C++》中,定义三种异常安全函数的保证
基本承诺如果异常被抛出,程序内部任何事物仍然保持在有效状态下,没有任何对象和数据结构会因此破坏。
强烈保证如果异常被抛出,那么程序状态不改变。要么执行成功,如果不成功,程序也会恢复到调用该函数之前的状态。可以认为是原子性。
不抛掷保证承诺绝对不抛出异常,函数一定能完成原先承诺的功能。
除非你真的需要在应用程序中泄漏资源和执行过程中带着被破坏的数据(比如测试代码的时候),否则都应该提供异常安全保证。
我们重新排列函数的语句
这里如果 new Image 执行失败,bgImage.reset 就不会执行,保证了 bgImage 的安全,数据破坏被避免,但仍然没有提供了强烈保证。因为参数 imgSrc,如果 new Image 抛出异常,输入流(istream)可能被移走,这个动作也是一种程序可见状态的改变,所以只是基本异常安全保证。
前边介绍了一种 copy and swap 的方式可以有效提高安全性至强烈安全,就是你把你要在函数中修改的部分 copy 一份,然后对 copy 的那一份进行操作,如果没报错就将该副本和原对象进行交换。
这样就实现了强烈保证,但这一方法不是永远有效,因为在一个函数中很多时候要调用其他函数,如果其他函数不是强烈保证的异常安全函数,就算你在该函数中保证也没用。就好比本例子中,如果 swap 函数不是强烈保证的异常安全函数,即使按照 copy and swap 的方法写出来的函数也不是强烈保证的异常安全函数,如果 swap 改变了状态,我们也无从下手。我们写出来的异常安全函数的安全等级是整个函数中所有部分的安全等级中取安全等级最低为准。
总结:
异常安全函数即使发生异常也不允许泄漏资源和数据结构破坏。
强烈保证大部分时候可以通过 copy and swap 来实现,但不总是奏效。
函数提高的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。