天天看点

【Effective C++读书笔记】篇十二(条款29~条款30)

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

早在条款14,我们就学习了如何使用智能指针来管理资源,这就是处理异常的一种常用手段。

当异常被抛出时,带有异常安全性的函数会:

1)不泄露任何资源:可以使用智能指针保证;

2)不允许数据破坏:在资源不泄露使用智能指针来保证的前提下,我们可以专注解决该项。

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

1)基本保证:如果异常被抛出,程序内的任何实物任然保持在有效状态之下,但在什么状态下我们却无从得知;

2)强烈保证:如果异常被抛出,程序状态不变。调用这样的函数需有这样的认知:如果函数成功,就是完全成功,如果失败,程序就恢复到“调用函数之前”的状态;

3)不抛掷保证:承诺绝不会抛出异常,因为它们总是能够完成他们原想承诺的功能。作用于内置类型(例如 int,指针等等)身上的所有操作都提供 nothrow 保证。这是异常安全码中一个必不可少的关键基础材料。

提供不抛掷保证是很难的,因此我们要尽量提供强烈保证。copy-and-swap是一个不错的方案。

但当“强烈保证”不切实际时,你就必须提供“基本保证”。

请记住:

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

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

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

条款30:透彻了解 inlining 的里里外外                                                                             

内联函数,多棒的点子!

之前对内联函数的了解太少,学习了条款30后,我才对内联有了更深刻的认识。

优点:将函数代码在编译时直接嵌入其调用处,避免了函数调用的代价。

缺点:

1、造成代码膨胀;

2、难以升级:由于内联函数是在编译阶段被嵌入的,所以如果代码升级,需要重新编译所有用到该内联函数的文件;

3、大部分调试器面对内联函数都无计可施:因为你无法在一个并不存在的函数内设立断点,因此很多建置环境仅仅只能“在调试版程序中禁止发生内联”。

内联有隐喻提出:定义于class定义式内的函数;也有明确提出:使用 inline 关键字。但请始终记得:inline 只是对编译器的一个申请,不是强制命令。

我们发现 inline函数和模板函数通常都被定义于头文件内。

内联函数是因为对于大多数编译器,内联函数的代码嵌入过程发生在编译时期,此时编译器需要知道内联函数的样子。

模板函数是因为编译器是在编译时期将模板函数具体化的,需要知道模板函数的样子。

有时候即使编译器有意将某个函数内联化,但当我们要取该函数指针时,该函数还是会生成一个非内联的函数本体。

构造函数和析构函数可以是内联函数,但请一定谨慎,他们往往是糟糕的内联候选人,由于构造函数(析构函数)的层层调用以及编译器为异常安全而默默添加的代码,很有可能造成代码迅速膨胀。

请记住:

1、将大多数内联限制在小型、被频繁调用的函数身上。

2、不要只因为模板函数被定义在头文件,就将它们声明为内联。

继续阅读