26.尽可能延后变量定义式的出现时间
- 尽可能延后变量的定义知道能够给它赋初值实参为止,这样做可以增加程序的清晰度并改善程序效率。否则会付出不必要的构造和析构成本。
- 当遇到循环时:若一个class的一个赋值成本低于一组构造和析构成本,在循环外声明变量,尤其是循环次数很多的时候。否则应在循环里声明变量。
27.尽量少做转型动作
- const_cast通常被用来将对象的常量性移除
dynamic_cast主要用来执行“安全向下转型”,可能耗费巨大运行成本
reinterpret_cast意图执行低级转型,实际动作(及结果)取决于编译器(即表明其不可移植)
static_cast用来强迫因式转换,例如将non-const转化为const对象,或讲int转化为double等,但无法将const转化为non-const,将point-to-base转为point-to-derived,将void*转化为typed指针
- 单一对象(例如一个derived对象)可能拥有一个以上的地址(例如“以base*指向它时的地址”和“以derived*指向它的地址”)
- 在子类虚函数中调用父类的同名虚函数(父类名::函数名(参数列表))
28.避免返回handles指向对象内部成分(成员变量和不被公开使用的成员函数)
- 成员变量的封装性最多只等于“返回其reference”的函数的返回级别(成员变量是private,但是函数返回其reference,则该变量相当于一个public的变量)
- 如果const成员函数传出一个reference,后者所指数据与对象自身有关联,而它又被存储于对象之外,那么这个函数的调用者可以修改那笔数据
- 不被公开使用的成员函数也是对象“内部”的一部分,因此也应留心不要返回它们的handles.(不应该令成员函数返回一个指针指向“访问级别较低”的成员函数,否则后者的实际访问级别就会提前如前者,返回“代表对象内部”的handles,可能导致空悬或虚吊,临时变量被销毁导致)
29.为“异常安全性”而努力是值得的
- 异常安全函数即使发生异常也不会邪路资源或允许任何数据结构败坏。这样的函数分为三种可能的保证:基本型、强烈性、不抛任何异常型。
- “强烈保证”往往能够以copy-andswap实现出来,但“强烈保证”并非对所有函数都可实现或具备现实意义
- 函数提供的“异常安全保证”通常最高只等于其所调用的各个函数的“异常安全保证”中的最弱者
30.透彻了解inlining的里里外外
- inlining 在大多数 C++ 程序中是编译期的行为;
- inline 函数是否真正 inline,取决于编译器;大部分编译器拒绝太过复杂(如带有循环或递归)的函数 inlining,而所有对 virtual 函数的调用(除非是最平淡无奇的)也都会使 inlining 落空;
- inline 造成的代码膨胀可能带来效率损失;inline 函数无法随着程序库的升级而升级
31.将文件间的编译依存关系降至最低
- 如果使用 object references 或 object pointers 可以完成任务,就不要使用 objects;
- 如果能够,尽量以 class 声明式替换 class 定义式;为声明式和定义式提供不同的头文件)
string不是class,是一个typedef (定义为basic_string<char>)
Handle classes和Interface class解除了接口和实现之间的耦合关系,从而降低文件之间的编译依存性。相依于声明式,不要想依于定义式。
程序库头文件应该以“完全且仅有声明式”的形式存在,这种做法无论是否涉及templates都适用。
32.确定你的public继承塑模出is-a关系
- “public继承”意味is-a,适用于base class身上的每一件事也一定适用于derived class上,因为每一个derived class对象也都是一个base class对象
33.避免遮掩继承而来的名称
内层作用域的名称会遮掩外层作用域的名称,无论名称类型是否相同。
derived class内的名称会遮掩base class内的名称,在public继承下不希望如此。
为使用被遮掩了的名称(在derived class中调用父类的同名函数),可以使用using声明式或转交函数。
34.区分接口继承和实现继承
- 接口继承和实现继承不同,在public继承之下,derived class总是继承base class的接口
- pure virtual函数只具体指定接口继承
简朴的impure virtual函数具体指定接口继承及缺省实现继承
non-virtual函数具体指定接口继承以及强制性实现继承
35.考虑virtual函数以外的其他继承
NVI手法(non-virtual interface):TemplateMethod设计模式 的一个独特表现形式。令客户通过public non-virtual成员函数间接调用private virtual函数。它以public non-virtual成员函数包裹较低访问性的virtual函数
- Strategy设计模式
将virtual函数替换为“函数指针成员变量”,这是Strategy设计模式的一种分解表现形式
以tr1::function成员变量替换virtual函数,因为允许使用任何可调用物搭配一个兼容于需求的签名式,这也是Strategy设计模式的某种表现形式
将继承体系内的virtual函数替换为另一个继承体系内的virtual函数。这是Strategy设计模式的传统实现手法
36.绝不重新定义继承而来的non-virtual函数
class D:public B{…}D中重定义了B中继承而来的non-virtual函数mf,会出现以下情况
D x;
B* pb=&x;
P* pd=&x;
pB->mf();//调用B中的mf
pD->mf();//调用D中的mf
假设mf是virtual函数,则pB->mf()、pD->mf()都会调动pD->mf函数
37.绝不重新定义继承而来的缺省参数值
virtual函数是动态绑定(后期绑定),缺省参数值是静态绑定(前期绑定)
否则可能会发生“在调用一个定义于derived class内的virtual函数”的同时,却适用base class为它所指定的缺省参数值
38.通过复合塑模处has-a或“根据某物实现出”
复合的意义与public继承完全不同
在应用域,复合意味着has –a,在实现域复合以为is-implemented-in-terms-of(根据某物实现出)
39.明智而审慎地使用private继承
- 如果classes之间的继承关系是private,编译器不会自动将一个derived class对象转化为一个base class对象。
- 由private base class继承而来的所有成员,在derived class中都会变成private属性,即使它们在base class中原本是protected或public属性。
- 尽可能使用复合,必要时才是用private继承(当protected成员或virtual函数牵扯进来的时候)
class Empty{} Empty p; // sizeof(p)=1;凡是独立对象都必须有非零大小
当两个并不存在is-a关系的两个class,其中一个需要访问另一个的protected成员,或需要重新定义其一或多个virtual函数,private继承可以考虑。
private继承可以造成empty base最优化
40.明智而审慎地使用多重继承
- virtual继承的成本:
使用virtual继承的那些classes所产生的对象往往比使用non-virtual继承的对象体积大,访问virtual base classes的成员变量时,也比访问non-virtual base classes的成员变量速度慢,支持“virtual base classes初始化”的规则比起non-virtual bases的情况远为复杂且不直观
非必要不使用virtual bases
如果必须使用virtual base classes尽可能避免在其中放置数据。