item32 确定你的public继承塑模出is-a关系
:::info
is-a关系是什么?
可以理解成一种类似集合的子集的概念——基类包含于派生类中
所有对基类可以进行的操作都可以施加在派生类上,否则说明这种继承关系是错误的。
:::
class Bird{
void fly();
}
class Penguin:public Bird{//企鹅是一种鸟
//但是企鹅不会飞,这种继承关系有误
}
class之间的关系: :::tips
- is-a
- has-a
- is-implemented-in-terms-of(根据某物出现)
:::
item33 避免遮掩继承而来的名称
内层作用域名称会遮掩外围作用域的名称
实际上对于基类和派生类也存在这种问题:
- 派生类的作用域被嵌套在基类中
- 派生类如果重载了基类的函数,那么基类的函数在派生类中会被遮蔽掉
避免方式:
用using声明式using Base::mf1
使用转交函数
class Base{
public:
virtual void mf1()=0;
virtual void mf1(int);
};
class Derived:private Base{
public:
virtual void mf1(){
Base::mf1();
}
}
:::tips
- derived classes内名称会遮掩base classes内的名称。
-
item34 区分接口继承和实现继承
成员函数的接口总会被继承
- 春虚函数的目的是为了让派生类只继承函数接口
- 非纯虚函数的目的是让派生类能继承接口和缺省实现
但是利用非纯虚函数时,客户可能会忘记声明和定义,这样会调用缺省的实现
避免这种问题的方式:
切断virtual函数和缺省实现之间的连接
class Airplane{
public:
virtual void fly(const Airport& destination)=0;
protected:
void defaultFly(const Airport& destination){
fly to someplace;
}
}
将函数声明为纯虚函数,强迫客户实现,同时提供一个缺省的实现
class ModelA:public Airplane{
public:
virtual void fly(const Airport& destination){
defaultFly(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);//
}
}
:::info 可以使用纯虚函数的定义来替代上版本的protected函数
本质上讲fly被分割成两个部分:
- 声明部分表示的是接口,这是派生类必须继承的
- 定义部分表示的是缺省实现,只有当明确提出申请时才会使用:
Airplane::fly(destination)
不能将这两个部分合并,因为这样就丧失了让两个函数享有不同的保护级别的机制 :::
:::tips