《Effective C++》知识点(5)--实现

26. 尽可能延后变量定义式的出现时间

      26.1 "尽可能延后"的真正意义是:你不只应该延后变量的定义,直到非得使用该变量的前一刻                为止,甚至应该尝试延后这份定义直到能够给它初值实参为止。

          a. 提高了程序效率。不仅避免了构造和析构非必要对象,还可以避免无意义的default构造。

          b. 提高了代码清晰度。以"具有明显意义之初值"将变量初始化,还可以附带说明变量的目                    的。

      26.2 对于循环,对象是应该定义在循环外还是循环内?

              如果类的一个赋值成本低于一组构造+析构成本,且这段代码对性能敏感,对象应该定义                在循环外。  否则对象应该定义在循环体内。

27. 尽量少做转型动作

      27.1 四种新式转型

作用说明
const_cast移除对象的const属性唯一
dynamic_cast安全向下转型非常消耗运行成本,可能转换失败
reinterpret_cast低级转型取决于编译器,不可移植
static_cast强迫隐式转换

      27.2 几个原则

          a. 如果可以,尽量避免转型,特别是注重效率的代码中避免dynamic_cast。如果有个设计

              需要转型动作,试着使用无需转型的替代设计,如"使用类型安全容器"或"将虚函数往继承

              体系的基类移动"。

          b. 如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要

              将转型放进他们自己的代码内。

          c. 宁可使用新式转型,不要使用旧式转型。前者很容易辨识出来,而且有分门别类的职责。

28. 避免返回handles指向对象内部成分

      避免返回handles (包括引用、指针、迭代器)指向对象内部。遵守这个条款可以

      a. 增加封装性。

      b. 帮助const成员函数的行为像个const,只提供只读权限。

      c. 将发生"空悬号码牌"(dangling handles)的可能性降至最低。dangling handles是指指向不复

         存在的对象,最常见的来源是函数返回值(临时对象)。

29. 为"异常安全"而努力是值得的

      29.1 "异常安全"的两个条件

            a. 不泄漏任何资源

            b. 不容许任何数据被破坏 

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

定义说明
不抛掷(nothrow)承诺不抛出异常,因为它们总能完成它们原先承诺的功能。优先选择,但大部分函数往往在基本承诺和强烈保证之间选择。
强烈保证

如果异常被抛出,程序状态不改变。如果函数调用失败,程序会恢复到调用函数之前的状态。  

可以用copy-and-swap实现,但并非对所有函数都可实现或具备现实意义(效率和复杂度带来的成本)
基本承诺如果异常被抛出,程序内的任何事物仍然保持在有效状态下。当强烈保证不切实际时,必须提供基本保证。

      29.3 编写"异常安全函数"的步骤

              首先是"以对象管理资源",避免资源泄漏。然后是挑选三个"异常安全保证"中的某一个实

              施于你所写的每一个函数上。应该挑选"现实可实现"条件下的最强烈等级,只有当你的函

              数调用了传统代码,才别无选择地将它设为"无任何保证"。将你的决定写成文档,供函数

              用户和将来的维护者参考。函数的"异常安全保证"是其可见接口的一部分,所以你应该慎

              重选择。

      29.4 函数提供的"异常安全保证"通常最高只等于其内部所调用之各个函数的"异常安全保证"中

              的最弱者。

      29.5 一般性规则:较少的代码是较好的代码,因为出错的机会比较少。

      29.6 不要为了表示某件事情发生而改变对象状态,除非那件事情真的发生了。

30. 透彻了解inlining的里里外外

      30.1 inline函数的优点

              看起来像函数,动作像函数,但没有函数调用的开销,比宏好得多。

      30.2 inline函数的缺点

              过多的inlining,会造成程序体积太大,代码膨胀可能导致额外的换页行为,降低指令高速

              缓存装置的命中率,以及伴随这些而来的效率损失。

               另外,inline函数如果升级,客户端程序都必须重新编译。

               inline函数不方便调试。

       30.3 注意事项

            a. inline只是对编译器的一个申请,不是强制命令。函数是否真的inline取决于编译器。

            b. inline是显式申请,而将函数体定义在class定义内是隐式申请。

            c. 不要因为function templates出现在头文件,就将它们声明为inline。 

            d. inlining在大多数c++程序中是编译期行为。

            e. 大部分编译器拒绝将太过复杂(如带有循环或递归)的函数或虚函数 inlining。编译器通常

                不对"通过函数指针而进行的调用"实施inlining。

            f. 构造函数和析构函数不应该是inline函数。即使是一个空的构造函数,编译器为其生成的

                代码也可能是精致复杂的,因为需要处理异常来确保没有资源泄漏。

       30.4 一开始不要将任何函数声明为inline, 或将inlining限制在小型、被频繁调用的函数身上。

               这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程

               序的速度提升机会最大化。

31. 将文件间的编译依赖关系降至最低

       31.1 支持"编译依赖最小化"的一般方法是:依赖于声明式,不要依赖于定义式。

           a. 如果使用对象引用或对象指针可以完成任务,就不要使用对象。

           b. 如果能够,尽量以 class 声明式替换 class 定义式。

           c. 为声明式和定义式提供不同的头文件。

       31.2 两个手段Handle classes 和 Interface classes

              思路都是将接口和实现分离。

实现开销
Handle classes将所有函数转交给相应的实现类,并由后者完成实际工作。

1.访问间接性

2. 初始化时动态内存分配,且有内存不足风险

3. 释放内存

Interface classes

实现一个抽象基类(只有一个虚析构和一些纯虚函数)。用户通过工厂获得对象(基类)指针,并通过这个指针完成工作。

虚函数表指针的开销

       31.3 程序库头文件应该以"完全且仅有声明式"的形式存在。这种做法不论是否涉及templates

               都适用。  

      

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值