条款26:尽可能延后变量定义式的出现时间
std::string encryptPassword(conststd::string &password)
{
std::string encrypted;
encrypted=password;
encrypt(encrypted);
return encrypted;
}
这样定义即初始化encrypted付出的代价是一个构造函数和一个赋值函数。如果改用以下方式:
std::string encryptPassword(conststd::string & password)
{
std::string encrypted(password);
encrypt(encrypted);
return encrypted;
}
尽可能延后变量的定义,不仅能避免构造非必要对象,还可以避免无意义的默认构造行为。
n 尽可能延后变量定义式的出现。这样做可以增加程序的清晰度并改善程序效率。
条款27:尽量少做转型动作
n 如果可能,尽量避免转型,特别是在注重效率的代码中避免dynamic_cast。如果有个设计需要转型动作,试着发展无需转型的替代设计。
n 如果转型是必要的,试着将它隐藏于某个函数背后。客户随后可以调用该函数,而不需要将转型放进他们自己的代码内。
n 宁可使用C++的新式转型,不要使用旧式转换。前者很容易辨识出来,而且也比较有着分门别类的职掌。
条款28:避免返回handles指向对象内部成分
考虑如下类的定义:
class GUIObject{…}
const Rectangle boundingBox(const GUIObject&obj);
客户有可能这么使用这个函数:
GUIObject *pgo;
const Point *pUpperLeft=&(boundingBox(*pgo).upperLeft());
上述的代码赋值号右边返回的临时的temp对象,然后temp对象会被释放,这是pUpperLeft会变成悬垂的(dangling)!
n 避免返回handles指向对象内部,遵守这个条款可增加封装性,帮助const成员函数的行为像个const,并将发生dangling handles的可能性降到最低。
条款29:为异常安全而努力是值得的
异常安全函数提供三个保证之一:
基本保证:如果异常被抛出,程序内的任何事物仍然保持在有效状态下。没有任何对象或者数据结构会因此而破坏。
强烈保证:如果异常被抛出,程序状态不改变,调用这样的函数需有这样的认知:如果函数成功,就是完全成功,如果函数失败,程序会回复到调用函数之前的状态。
不抛出保证:承诺绝不抛出异常,因为它们总是能够完成它们原先承诺的功能。作用于内置类型身上的所有操作都提供不抛出保证。
条款30:透彻了解inlining的里里外外
将大多数inlining限制在小型被频繁调用的函数身上,这可使日后的调试过程和二进制升级更容易,也可使潜在的代码膨胀问题最小化,使程序的速度提升机会最大化。
不要只因为函数模板出现在头文件,就将它们声明为inline.
条款31:将文件间的编译依存关系降到最低
n 如果使用对象的引用或者指针可以完成任务,就不要使用对象
n 如果能够,尽量以class声明代替class定义式
n 为声明和定义式提供不同的头文件
条款32:确定你的public继承塑模出is-a关系
public继承意味着is-a,适用于baseclasses身上的每一件事情一定也适用于derived classess身上,因为每一个derived class对象也是一个base对象。
条款33:避免遮掩继承而来的名称
n 派生类内的名称会遮掩基类内的名称,在Public继承下从来没有人希望如此。
n 为了让被遮掩的名称再见天日,可使用using声明或转交函数。
条款34:区分接口继承和实现继承
n 接口继承和实现继承不同,在Public继承之下,派生类总是继承基类的接口。
n 纯虚函数只具体指定接口继承
n 非纯虚函数具体指定接口继承及缺省实现继承。
n 非虚函数具体指定接口继承以及强制性实现继承
条款35:考虑虚函数以外的其它选择
条款36:绝不重新定义继承而来的非虚函数
public继承下,使用与基类对象的每一件事,也适用于派生类对象,因为每个派生类对象都是一个基类对象,派生类一定会继承非虚函数的接口和实现
条款37:绝不重新定义继承而来的缺省参数值
考虑以下类:
class Shape
{
public:
enumShapeColor{Red,Green,Blue};
virtualvoid draw(ShapeColor color=Red) const =0;
};
class Rectangle:public Shape
{
public:
virtualvoid draw(ShapeColor color=Green) const;
};
如果定义以下变量:
Shape *p=new Rectangle;
p->draw(); //这一行实际调用的是Rectangle::draw(Shape::Red)
virtual函数是动态绑定,而缺省参数值却是静态绑定,意思是你可能在调用一个定义于派生类内的虚函数的同时,却使用基类为它所指定的缺省参数值。
条款38:通过复合塑模出has-a或者根据某物实现出
考虑实现一个set容器,假如使用链表,可以定义类如下:
class set
{
public:
voidinsert(const T& item);
voidremove(const T&item);
…..
private:
std::list<T>rep;
};
这里set和list即为has-a关系。
n 复合的意义和public继承完全不同。
n 在应用域,复合意味着has-a。在实现域,复合意味着根据某物实现出。
条款39:明智而审慎地使用private继承
考虑一个空白类:
class Empty{};
class HoldsAnInt
{
private:
intx;
Emptye;
};
这时sizeof(HoldsAnInt)>sizeof(int);因为空类的对象e也会占据内存大小。
使用下面的替代实现:
class HoldsAnInt:private Empty
{
private:
intx;
};
这时sizeof(HoldsAnInt)==4;
private 继承,使得空白基类最优化,缩小了派生类对象的大小。
n private继承意味着根据某物实现出。
n 和复合不同,Private继承可以造成空的基类最优化,这对致力于对象尺寸最小化的程序库开发者而言,可能很重要。
条款40:明智而审慎地使用多重继承
使用virtual继承的那些类所产生的对象往往比使用非虚继承的兄弟们体积大,访问虚基类的成员变量时,也比访问非虚基类的成员变量速度慢。种种细节因编译器不同而异,但基本重点很清楚,你得为虚继承付出代价。