600字范文,内容丰富有趣,生活中的好帮手!
600字范文 > 【Effection C++】读书笔记 条款29~条款30

【Effection C++】读书笔记 条款29~条款30

时间:2020-09-07 20:20:38

相关推荐

【Effection C++】读书笔记 条款29~条款30

【Effective C++】读书笔记 Part5 实现

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

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

不泄漏任何资源。不允许数据败坏。

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

基本承诺。如果异常被抛出,程序内的任何事物仍然在有效状态下。没有任何对象或数据结构会因此而败坏。所有对象都处于一种内部前后一致的状态,但是程序中哦个对象的现实状态不可预料。强烈保证。如果异常被抛出,程序状态不会被改变。调用这样的函数只有如下两种情况:函数调用成功,就是完全成功,如果函数调用失败,程序会回复到“调用函数之前”的状态。不抛出异常。函数保证绝不抛出异常,因为它们总能够完成它们所承诺的功能。

对于任何函数,都应该尽量保证异常安全,注意下面亮点:

不要为了表示某件事情发生而改变对象状态,除非那件事情已经真的发生了。copy-swap策略,往往能够帮助我们实现“强烈保证”的异常安全,但是“强烈保证”并非堆所有函数都可实现或具备现实意义,有因为效率和复杂度的关系,“基本承诺”的异常安全就已经够用了。函数提供的的“异常安全保证”通常最高只等于其所调用之各个函数的“异常安全保证”中的最弱者。

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

1. 基础知识

Inline函数会给我们带来许多的好处,它们看起来像函数,动作函数,比洪好的多,可以调用它们但是又不需要承受函数调用所招致的额外开销。编译器最优化设计通常被设计用来浓缩那些“不含函数调用”的代码,所以当使用inline函数时候,编译器就有能力对它执行语境相关最优化。

inline函数通常是将相应的函数本体直接展开在调用点,而舍弃了函数调用。如果函数本体过大,这就会导致目标代码(object code)增大,造成代码膨胀。但是如果inline函数本体很小,编译器针对“函数本体”所产生的目标代码可能比针对“函数调用”产生的目标代码所产出的目标代码更小,同时也拥有了inline函数所带来的效率。

2. inline函数的inlining

inline关键字可以用来修饰普通函数和类的成员函数。对于inline普通函数通常需要将函数定义及其实现放置在头文件中,因为对于大多数build environment而言,都是在编译过程中进行inlining(inline展开),所以对于一个inline函数或者是inline成员函数,都需要定义在头文件内,从而保证编译器在编译阶段就已经拥有了inline函数的定义。

3. 编译器视情况来选择是否inlining

除此之外,inline函数只是向编译器提出一个申请,编译器可以选择忽略。大多数编译器都会拒绝将太过复杂的函数(如带有循环或者递归)进行inlining,而对于所有虚函数也都会拒绝inlining。

由于对于所有的虚函数而言,显然只有在运行时候才能够确定具体执行的函数本体,但是对于inline函数而言,则是在编译阶段就需要了解到函数的具体定义。显然对于virtual函数,编译器无法将其inlining。

除此之外,inline函数(非成员函数)其作用于一般都是在文件作用域内,因为inline函数如果都能够在调用处展开,编译器也没有必要对此函数创建符号,并且在代码段中存储函数的二进制指令(函数本体)。

但是,有些情况下,虽然编译器能够对函数进行inlining,但是也会生成一个函数本体。

如当我们对一个inline函数进行取地址的时候,这时候就强迫编译器生成一个相应的outlined函数本体。

另外,如果我们通过一个函数指针来调用inline函数,编译器往往不会将其inlining,因此根据调用函数的方式不同,编译器会视情况来决定是否inlining。

inline void f(){……}//假设编译器会inline函数fvoid ( *pf)()=f;//指向函数的指针f();//这个调用会被inlined,因为是正常调用pf();//这个调用可能不会被inlined,因为它是通过函数指针达成

4. inline函数与构造和析构函数

构造函数和析构函数往往都不适合作为inline函数。虽然在许多情况下,构造函数的函数体都可能十分小,或者为空。

但是看看C++标准对于对象被构建和销毁时候所保证的的事情:

当你使用new的时候,动态创建的对象被构造函数自动初始化 。当你使用delete的时候,对应的析构函数将被调用。当你创建一个对象的时候,其每一个base class及其每一个成员变量都会被自动构造。当你销毁一个对象的时候,反向顺序的析构行为会自动发生。如果有个异常在构造函数内抛出,该对象已经构造好的那一部分会自动析构。

等等。

这就注定了本来可能函数体非常小的构造函数和析构函数,在经过编译器的处理后就会变得非常复杂,显然就不适合将其进行inlining。

5. inline函数与函数升级

对于程序库中如果不存在non-inline函数,那么客户端程序一般只需要重新链接即可,并不需要重新编译。

如果客户端程序动态链接程序库,那么可能就无需做任何改变就可直接使用升级了版本的程序库。

但是如果将程序库设计为inline函数会给我们的客户端程序带来一定的冲击。主要是客户端程序中,将包含我们程序库中的inline函数的展开,因而如果程序库升级了inline函数,那么客户端代码也必须要重新编译。这个过程量就会显著增大。

6. inline函数与调试

最后一点要注意的是,inline函数往往无法和调试器完美配合,因为在代码执行中,inline函数已经被展开,显然你无法在一个已经不存在的函数设置断点。

7. 小结

请将大多数inline函数限制在小型,被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使得程序的速度提升机会最大化。不要只是因为function template出现在头文件中,就将它们声明为inline.

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。