条款26 尽量延后变量的定义式出现的时间
- 不仅应该延后变量的定义,更应该直到使用该变量的前一刻为止,甚至应该尝试延后这份定义直到能够给它初始值为止。如果这样,不仅能够避免构造和析构的非必要操作,还可以避免毫无意义的调用默认构造函数带来的开销。
条款27 尽量少做转型动作
- 尽量以C++风格的转型替代C风格的转型,前者更加安全。
- const_cast 通常用来将对象的常量性转除。它是唯一有此能力的C++-style转型操作符。
- dynamic_cast用来执行继承体系中安全的向下转型或跨系转型动作。也就是说你可以利用它将指向基类对象的指针或者引用转型为指向派生类对象的指针或引用,并得知转型是否成功。如果转型失败,会以一个null指针(当转型对象是指针)或一个exception(当转型对象是引用)表现出来。dynamic_cast是唯一无法由旧式语法执行的转型动作,也是唯一可能消耗重大运行成本的转型动作。
- static_cast 基本上拥有与C旧式转型相同的威力与意义,以及相同的限制。例如将一个非 const 的对象转换为 const 对象,或将int 转换为 double等等。它也可以用来执行上述多种转换的反向转换,例如将void*指针转为typed指针,将pointer-to-base转为pointer-to-derived。但是他无法将const转为non-const,这个只有const-cast才能够办到。
- reinterpret_cast意图执行低级转型,实际动作及结果可能取决于编译器,这也就表示它不可移植
条款28 避免返回一个handles指向对象的内部成分
- 当类定义了一个函数来返回其内部数据的一个指针或者引用的时候,此时会破坏数据的封装性
class CTest
{
public:
CTest()
{
pValue = new int(0);
printf("new:0x%08x\n", pValue);
}
~CTest()
{
printf("delete:0x%08x\n", pValue);
delete pValue;
}
const int* GetValue() const {return pValue;}
private:
int *pValue;
};
CTest Fun()
{
return CTest();
}
int main()
{
int* pTem = const_cast<int*>(Fun().GetValue());
printf("main:0x%08x\n", pTem);
*pTem = 100;//其实这里访问的内存是未分配的,vs2010下却不会崩溃
return 0;
}
/*
输出:
new:0x00033690
delete:0x00033690
main:0x00033690
*/
const int* GetValue() const {return pValue;}
虽然用const来保证返回的指针是可读的,但是无法保证用户会对这个指针干出什么事情,更大的问题是比如上述的Fun函数,会导致pTem变为空悬指针而导致访问异常
- 比如operator[] 应该返回类内部对象的引用,但这些函数只是特例情况
条款29 为异常安全而努力是值得的
- 本条款还是看书比较好,暂不归纳
条款30 透彻了解inline的里里外外
- 将大多数inline限制在小型的、被频繁调用的函数身上,可以使代码膨胀问题最小化,使程序速度提升最大化。
- inline函数是编译时期的,可能会对inline函数的调试产生问题。
- 不能滥用inline,滥用inline可能会导致程序体积过大,也可能导致额外的换页行为,降低指令高速缓冲装置的击中率,以及伴随而来的效率问题。
条款31 将文件间的编译依存关系降至最低
- 支持编译依存最小化的一般构思是:相依于声明式而非定义式
条款32 确定你的public继承塑模出is-a关系
- public继承意味着is-a的关系,适用在基类上的方法都能用于派生类上。
条款33 避免遮掩继承而来的名称
- 在public继承体系中,派生类和基类的关系是is-a的关系,所以派生类中不应该隐藏基类内的名称
- 为了在派生类中重载基类的函数,可以使用using声明式,或者在派生类的函数中显示调用基类的函数。
条款34 区分接口继承和实现继承
- 接口继承与实现继承不同。在public继承体系下,派生类总是继承基类的接口。
- 纯虚函数只具体指定接口继承。
- 一般的虚函数具体指定接口继承和缺省实现继承。
- 非虚函数具体指定接口继承以及强制性实现继承。(由条款33可知,在public继承中,不要在派生类中隐藏基类的非虚函数)
条款35 考虑virtual函数以外的其他选择
- 本条款还是看书比较好,暂不归纳
条款36 绝不重新定义继承而来的非虚函数
- 若打破这个规则,则基类与派生类在某个指定的非虚函数上的表现形式就会不一致,违反了is-a的关系。非虚函数的调用不具有多态性质。
条款37 绝不重新定义继承而来的缺省参数值
- virtual函数是会动态绑定的,而其缺省参数值却是静态绑定的。若虚要不同缺省参数值的虚函数,可以考虑条款36提供的方法
class CFather
{
public:
virtual void FunTest(int nValue = 4096)
{
printf("Fahter:%d\n", nValue);
}
};
class CChild : public CFather
{
public:
CChild(){}
public:
void FunTest1(int nValue = 0)
{
FunTest(nValue);
}
private:
virtual void FunTest(int nValue)//不提供默认参数
{
printf("Child:%d\n", nValue);
}
};
int main()
{
CChild Child;
Child.FunTest1(); //Child:0
return 0;
}
条款38 通过复合塑模出"has-a"或"根据某物实现出"
- 复合的意义和public继承完全不同
class CTest0
{
};
class CTest1
{
private:
CTest0 Test0; //这就是复合
};
条款39 明确而谨慎的使用private继承
- 如果类间的关系是private继承,编译器不会自动将派生类对象转换为基类对象
- 由private base class继承而来的所有public protected成员在派生类中都变为private属性
- private继承意味着"根据某物实现出"。让D以private方式继承C,用意是为了让D采用C类以及具有的某些特性,而不是因为C和D存在任和观念上的关系。private继承纯粹只是一种实现技术。private在软件设计层面没有任何意义,其意义只在软件实现层面
- private和复合都意味着"根据某物实现出",一般情况下尽可能使用复合而不是private继承。主要当protected成员或者virtual函数牵扯进来的时候或者是继承一个空类(虽然空类不定义任何成员,但编译器一般会插入一个char,此时使用复合会让正在构造的类变大。当一个类私有继承一个空类的时候,且此类又有别的数据成员的时候,空类的char并不会占用派生类的空间),才考虑使用private继承
条款40 明智而谨慎的使用多重继承
- 实际项目开发,尚未遇见需要用到多重继承的情况,暂不进行细究
网友评论