Chapter6
条款32:确定你的public
继承塑模出is-a
关系
- "public继承"意味
is-a
.适用于base classes
身上的每一件事情一定也适用于derived classes
身上,因为每一个derived class
对象也都是一个base class
对象.
条款33:避免遮掩继承而来的名称
derived classes
内的名称会遮掩base class
内的名称。只要名称相同就会被覆盖,无论函数参数是否相同,也无论是virtual
还是non-virtual
(见下例)- 为了让被遮掩的名称再见天日,可使用
using
声明式或转交函数(forwarding functions)
class Base{
private:
int x;
public:
virtual void mf1() =0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived:public Base{
public:
virtual void mf1();
void mf3();
void mf4();
...
};
int main(){
Derived d;
int x;
...
d.mf1();
d.mf1(x);
d.mf2();
d.mf3();
d.mf3(x);
}
class Base{
private:
int x;
public:
virtual void mf1() =0;
virtual void mf1(int);
virtual void mf2();
void mf3();
void mf3(double);
...
};
class Derived:public Base{
public:
using Base::mf1();
using Base::mf3();
virtual void mf1();
void mf3();
void mf4();
};
int main(){
Derived d;
int x;
...
d.mf1();
d.mf1(x);
d.mf2();
d.mf3();
d.mf3(x);
}
条款34:区分接口继承和实现继承
public
继承包含两个方面的概念:函数接口(function interfaces)继承和函数实现(function implementations)继承
- 声明一个
pure virtual
函数的目的是为了让dervied classes
只继承函数接口 - 声明简朴的(非纯)
impure virtual
函数的目的,是让derived classes
继承该函数的接口和缺省实现 - 声明
non-virtual
函数的目的是为了令derived classes
继承函数的接口及一份强制性实现。任何derived class
都不应该尝试改变继承而来的non-virtual
函数
class Shape{
public:
virtual void draw() const = 0;
};
class Airport {...};
class Airplane{
public:
virtual void fly(const Airport& destination);
};
void Airplane::fly(const Airport& destination){
}
class ModelA:public Airplane {...};
class ModelB:public Airplane {...};
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
...
protected:
void defaultFly(const Airport& destination);
};
void Airplane:defaultFly(const Airport& destination){
}
class ModelA:public Airplane{
public:
virtual void fly(const Airport& destination){
defaultFly(destination);
}
};
class ModelB:public Airplane{
public:
virtual void fly(const Airport& destination){
defaultFly(destination);
}
};
class ModelC:public Airplane{
public:
virtual void fly(const Airport& destination);
...
};
void ModelC::fly(const Airport& destination){
}
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
...
};
void AIrplane::fly(const Airport& destination)
{
}
class ModelA:public Airplane{
public:
virtual void fly(const Airport& destination){
Airplane::fly(destination);
}
...
};
class ModelB:public Airplane{
public:
virtual void fly(const Airport& destination){
Airplane::fly(destination);
}
};
class ModelC:public Airplane{
public:
virtual void fly(const Airport& destination);
...
};
void ModelC:fly(const Airport& destination){
}
条款35:考虑virtual
函数以外的其他选择
virtual
函数的替换方案有以下几种:
- 使用
non-virtual interface(NVI)
手法,那是Template Method
设计模式的一种特殊形式。它以public non-virtual
成员函数包裹较低访问性(private
或protected
)的virtual
函数 - 将
virtual
函数替换为“函数指针成员变量”,这是Strategy
设计模式的一种分解表现形式 - 以
function
成员变量替换virtual
函数,因而允许使用任何可调用物 - 将继承体系内的
virtual
函数替换为另一个继承体系内的virtual
函数。这是Strategy
设计模式的传统实现手法
class GameCharacter{
public:
int healthValue() const{
...
int retVal=doHealthValue();
...
}
...
private:
virtual int doHealthValue() const{
}
};
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter{
public:
typedef int (*HealthCalcFunc) (const GameCharacter&);
explict GameCharacter(HealthCalcFunc fcf=defaultHealthCalc):healthFunc(hcf)
{}
int healthValue() const{
return healthFunc(*this);
}
private:
HealthCalcFunc healthFunc;
};
class EvilBadGuy:public GameCharacter{
public:
explict EvilBadGuy(HealthCalcFunc hcf=defaultHealthCalc):GameCharacter(hcf)
{...}
...
};
int loseHealthQuickly(const GameCharacter&);
int loseHealthSlowly(const GameCharacter&);
EvilBadGuy ebg1(lossHealthQuickly);
EvilBadGuy ebg2(lossHealthSlowly);
class GameCharacter;
int defaultHealthCalc(const GameCharacter& gc);
class GameCharacter{
public:
typedef function<int(const GameCharacter&)> HealthCalcFunc;
explict GameCharacter(HealthCalcFunc fcf=defaultHealthCalc):healthFunc(hcf)
{}
int healthValue() const{
return healthFunc(*this);
}
private:
HealthCalcFunc healthFunc;
};
class EvilBadGuy:public GameCharacter{
public:
explict EvilBadGuy(HealthCalcFunc hcf=defaultHealthCalc):GameCharacter(hcf)
{...}
...
};
int loseHealthQuickly(const GameCharacter&);
int loseHealthSlowly(const GameCharacter&);
struct HealthCalculator{
int operator()(const GameCharacter&) const
{ ... }
};
EvilBadGuy ebg1(lossHealthQuickly);
EvilBadGuy ebg2(lossHealthSlowly);
EvilBadGuy ebg2(HealthCalculator());
class GameCharacter;
class HealthCalcFunc{
public:
...
virtual int calc(const GameCharacter& gc) const
{ ... }
...
};
HealthCalcFunc defaultHealthCalc;
Class GameCharacter{
public:
explict GameCharacter(HealthCalcFunc* phcf=&defaultHealthCalc):pHealthCalc(phcf)
{}
int healthValue() const
{ return pHealthCalc->calc(*this);}
private:
HealthCalcFunc* pHealthCalc;
};
条款36:绝不重新定义继承而来的non-virtual
函数
non-virtual
函数的不变性凌驾其特异性,所以任何情况下都不该重新定义一个继承而来的non-virtual
函数
条款37:绝不重新定义继承而来的缺省参数值
virtual
函数是动态绑定的,而缺省参数值却是静态绑定的。(即在多态中,永远使用的都是base class
的缺省参数,而不会使用derived class
的缺省参数)
条款38:通过复合塑模出has-a
或"根据某物实现出"
- 复合的意义和
public
继承完全不同 - 在应用域,复合意味
has-a
(有一个)。在实现域,复合意味is-implemented-in-terms-of
(根据某物实现出)(?)
条款39:明智而审慎地使用private
继承
private
继承意味implemented-in-terms-of
(根据某物实现出).private
继承意味只有实现部分被继承.
- 如果D以
pirvate
形式继承B,意思是D对象根据B对象实现而得,再没有其他意涵了
- 使用复合(
composition
)通常可以替代private
继承,所以尽可能使用复合,必要时才使用private
继承(见例子) - 和复合不同,
private
继承可以造成empty base
最优化。这对致力于“对象尺寸最小化”的程序库开发者而言,可能很重要(见例子)
class Timer{
public:
explicit Timer(int tickFrequency);
virtual void onTick() const;
...
};
class Widget:private timer{
private:
virtual void onTick() const;
};
class Widget{
private:
class WidgetTimer:public Timer{
public:
virtual void onTick() const;
...
};
WidgetTimer timer;
};
class Empty();
class HoldAnInt{
private:
int x;
Empty e;
};
sizeof(Empty())==> =1 !!!
sizeof(HoldAnInt)>sizeof(int)==> true
class HoldAnInt:private Empty{
private:
int x;
};
sizeof(HoldAnInt)==sizeof(int)==> true
条款40:明智而审慎地使用多重继承
- 多重继承比单一继承复杂。它可能导致新的歧义性,以及对
virtual
继承的需要 virtual
继承会增加大小、速度、初始化(及赋值)等等成本。如果virtual base classes
不带任何数据,将是最具实用价值的情况