Effective C++第五章 实现 条款26-条款31

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

不到迫不得已时,不定义变量。

在这里插入图片描述
上述代码的问题:如果上述函数体内部抛出了异常,那么定义的字符串变量没有任何的作用,白白消耗构造和析构的时间

改进版本:注意定义变量的位置,以及在定义变量的同时进行初始化(以避免默认构造函数的调用,具体见:条款4)
在这里插入图片描述


条款27.尽量少做转型动作

旧式转型:
在这里插入图片描述
新式转型:
在这里插入图片描述
总结:

  • 尽量避免转型,特别是在注重效率的代码中避免使用dynamic_cast,如果需要转型,尽量使用无需转型的方式来代替
  • 如果转型必要,设计相关函数,客户直接调用该函数完成转型,而不是在自己的代码中转型
  • 尽量使用新式的转型方式

条款28.避免返回handles(引用、指针、迭代器)指向对象内部成分

避免返回handles(包括引用、指针、迭代器)指向对象内部,这样可以增加封装性,帮助const成员函数的行为像个const,并将发生“虚吊号码牌”的可能性降到最低。

这里是引用
在这里插入图片描述
在这里插入图片描述
分析如下调用:
在这里插入图片描述
虽然upperLeft()是常数函数,在其内部不能改变成员变量的值,但是可以通过该函数返回的引用改变内部变量的值,发生此状况的原因在于返回了其内部成员的引用

导致空悬的号码牌(handles)
定义如下类:
在这里插入图片描述
调用该函数:
在这里插入图片描述
分析一下调用上述函数会带来什么问题:
首先通过调用boundingBox()返回一个Rectangle对象,我们称之为temp,然后调用upperLeft()返回一个Point()对象的引用,然后pUpperLeft指向该对象。这里看似一切正确,但是当该语句执行完后,temp对象会释放掉(析构),因为它是一个临时对象,最终导致pUpperLeft指向一个不存在的对象,成为空悬指针


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

异常安全函数:即使发生异常也不会泄漏资源允许任何数据结构败坏

异常安全保证:

  • 基本保证:如果抛出异常,程序内任何对象或其数据结构不会被破坏,所有的对象都处于一种内部前后一致的状态(但是我们并不知道处于哪种状态,只要这种状态合法就行,除非调用相关函数查看对象的状态)。
  • 强烈保证:如果异常抛出程序状态不会改变。如果函数调用成功,就是完全成功;如果函数失败,程序会恢复到“调用函数之前”的状态。
  • 不抛掷保证:承诺绝不抛出异常。作用于内置类型的所有操作都提供nothrow保证。

总结:

  • 强烈保证往往能够以copy-and-swap实现出来,但强烈保证并非对所有函数都可实现或具备现实意义
  • 函数提供的“异常安全保证”通常最高只等于其所调用的各个函数的“异常安全保证”中最弱的

条款30.透彻理解inlining的里里外外

使用内联函数的目的是为了提高程序的执行效率,解决一些频繁调用的小函数大量消耗栈内存空间的问题,但很多情况下并非如此。
总结:

  • 大多数内联函数限制在小型、被频繁调用的函数身上。这可以使得最小化代码膨胀问题,使程序的速度提升机会最大化
  • inline只适合函数体内代码简单的函数使用,不能包含复杂的结构控制语句(switch,while,for)或者函数体内的代码比较长,并且内联函数本身不能是直接递归函数
  • inline仅是对编译器的一个建议,最后是否真正内联,看编译器的意思。编译器如果认为函数不复杂,能在调用点展开,就会真正内联,并不是说声明了内联就一定会内联
  • 建议inline函数定义放在头文件内。因为内联函数在调用点进行展开,所以编译器必须随处可见内联函数的定义,每个调用内联函数的文件都出现了该内联函数的定义,因此,将其放在头文件中最合适
  • 定义在类中的成员函数,缺省情况下都是内联的。但是成员函数的定义放在类声明之中虽然不是不可以,但不是一种良好的编程风格,最好在类内进行声明,在类外进行定义。要特别注意:构造函数和析构函数的定义最好不要放在函数体内,它们可能会隐藏一些行为,例如执行了基类或者成员变量的构造函数或者析构函数,这些函数会产生大量的副本。
  • inline是一种用于实现的关键字。关键字inline必须与函数定义体放在一起才能使函数成为内联函数,如果放在函数声明的前面,其不起任何作用

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

编译依存:

有如下类的声明:
这里是引用
其包含的头文件:
在这里插入图片描述
分析:
如果头文件中有任何一个被改变或者这些头文件所依赖的其他头文件被改变,那么每一个包含Person类的头文件都需要重新编译,这样的连串编译依存关系会对许多项目造成难以形容的灾难


解决方法:将Person分成两个类,一个是接口类PersonImpl,一个是实现类Person
在这里插入图片描述
分析:这样设计,Person的客户就与Dates,Addresses以及Person的实现分离了,那些类的任何修改都不需要Person客户端重新编译

总结:

  • 支持“编译依存最小化的一般构想”是:相依于声明式,不要相依于定义式。基于此构想的两个手段:Handle classes 和 Interface classes
  • 程序库的头文件应该以“完全且仅有声明式”的形式存在。这种做法不论是否涉及templates都适用。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章