条款32:确定你的public继承塑模出”is-a“关系
条款33:避免遮掩继承而来的名称
derived class内的名称会遮掩base class内的名称
条款34:区分接口继承和实现继承
PS:可以为pure virtual函数提供定义,编译器不会报错
条款35:考虑virtual函数以外的其他选择

1.Non-Virtual Interface方法(NVI)
virtual函数为private,healthValue函数为public成员函数,调用private virtual函数,实际工作是在private virtual函数中完成的。这个non-virtual函数为virtual函数的外覆器
class Character
{
public:
int healthValue() const
{
...
int retVal = doHealthValue();
...
return retVal;
}
private:
virtual int doHealthValue() const
{...}
};
2.运用Function Pointers
class Character; //前置声明
int defaultHealthCalc(const GameCharacter& gc);
class Character
{
public:
typedef int (*HealthCalcFunc)(const Character&);
explicit Character(HealthCalcFunc hcf = defaultHealthCalc)
:healthFunc(hcf)
{}
int healthValue() const
{return healthFunc(*this);}
private:
HealthCalcFunc healthFunc;
};
优点
(1)同一人物类型之不同实体可以有不同的健康计算函数
(2)某已知人物之健康指数计算函数可在运行期变更
例如Character可提供一个成员函数setHealthCalculator,用来替换当前的生命计算函数
缺点
non-member函数无法访问non-public变量,可能需要降低class的封装性
3.利用tr1::function完成策略模式
class Character; //前置声明
int defaultHealthCalc(const GameCharacter& gc);
class Character
{
public:
//HealthCalcFunc可以是任何“可调用物”,
//可被调用并接受任何兼容于Character之物,返回任何兼容于int的东西
typedef std::tr1::function<int (const Character&)> HealthCalcFunc;
explicit Character(HealthCalcFunc hcf = defaultHealthCalc)
:healthFunc(hcf)
{}
int healthValue() const
{return healthFunc(*this);}
private:
HealthCalcFunc healthFunc;
};
条款36:绝不重新定义继承而来的non-virtual函数
任何情况下都不应该重新定义一个继承而来的non-virtual函数
条款37:绝不重新定义继承而来的缺省参数值
原因:virtual函数为动态绑定,但缺省参数值却是静态绑定
动态类型与静态类型的区别
Shape *ps;
Shape* pc = new Circle;
Shape* pr = new Rectangle;
ps,pc,pr都被声明为pointer-to-Shape类型,无论他们指向什么,静态类型都为Shape*
动态类型是“面前所指对象的类型”,即动态类型可以表现出一个对象将会有什么行为。因此pc动态类型为Circle*,ps没有动态类型,因为它没有指向任何类型
条款38:通过复合塑模出has-a或“根据某物实现出(is-implemented-in-terms-of)”
当复合发生在应用域内的对象之间,表现出has-a关系
当复合发生在实现域内的对象之间,表现出is-implemented-in-terms-of关系
条款39:明智而审慎的使用private继承
private继承意味着is-implemented-in-terms-of。如果你让class D以private方式继承class B,用意是为了采用class B内已经备妥的某些特性,而不是因为B和D存在任何观念上的关系。
private继承意味着只有实现部分被继承,接口部分略去。
更多推荐使用复合,而不是private继承
条款40:明智而审慎的使用多重继承
多重继承可能会出现“钻石型多重继承”,解决方法是利用virtual base class
使用虚继承缺点:
(1)体积大
(2)速度慢
(3)增加初始化成本,即class若派生自virtual base而需要初始化,必须追溯其virtual base--无论那些base距离多远
因此建议少用虚继承,如果使用,最好virtual base class不带数据
条款41:了解隐式接口和编译期多态
编译期多态:以不同的template参数具现化function templates 会导致调用不同的函数
显式接口:在class内,由函数的签名式(即函数名称、函数类型、返回类型)构成
隐式接口:由有效表达式组成
条款42:了解typename的双重意义
在使用模版时,class和typename大多情况下是没有区别的,但有时必须得使用typename
例:
template<typename C>
void funC(const C &c)
{
if(c.size()>2)
{
C::const_iterator iter(c.begin());
++iter;
}
}
template内出现的名称如果依赖于某个template参数,被称为从属名称,如例中的C::const_iterator
若从属名称在class内嵌套,则被称为嵌套从属名称,如C::const_iterator
不依赖于任何template参数的名称,叫做非从属名称
template<typename C>
void funC(const C &c)
{
C::const_iteraot * x;
}
上面代码中,如果C::const_iterator不是一个类型,而是C中的一个static成员变量const_iterator,或x是一个global变量,那么C::const_iteraot * x中的*号就是乘号的意义了。
在C++规则中,若解析器在template中遇到一个嵌套从属名称,便假设这名称不是一个类型,除非我们告诉它是。
解决此问题的方法是在前面加上typename
template<typename C>
void funC(const C &c)
{
typename C::const_iteraot * x;
}
例外情况:
typename不可以出现在base class List内的嵌套从属类型名称之前,也不可在member initialization list(成员初值列)中作为base class修饰符
template<typename T>
class D:public Base<T>::B //base class list中不允许typename
{
public:
D(int x)
:Base<T>::B //初值列中中不允许typename
{}
条款43:学习处理模板化基类内的名称
条款44:将与参数无关的代码抽离templates
条款45:运用成员函数模版接受所有兼容类型
使用成员函数模版来处理可接受所有兼容类型的函数,例如某class的构造函数,我们想要写出可以兼容所有类的构造函数,如A(int x),A(double x),A(vector<int> x)等,那么这时用到的就是泛型编程写出一个成员函数template
template<typename T>
class A
{
public:
template<typename U>
A(const A<U> &other); //成员函数模版,为了生成copy构造函数
};
PS:在class内声明泛化的copy构造函数(member template),并不会阻止编译器生成它们自己的copy构造函数,相同规则也适用于赋值操作符
条款46:需要类型转换时请为模版定义非成员函数
条款47:请使用traits class表现类型信息
条款48:认识template元编程
模版元编程可将工作由运行期移往编译期,因为得以实现早期错误侦测和更高的执行效率,但是编译期时间会增长
条款49:了解new-handler的行为
条款50:了解new和delete的合理替换机制
条款51:编写new和delete时需固守常规
条款52:写了placement new 也要写placement delete
条款53:不要轻忽编译器的警告
条款54:让自己熟悉包括TR1在内的标准程序库
条款55:让自己熟悉Boost