继承与面向对象设计
继承可以是virtual或non-virtual。
virtual函数意味接口必须被继承。
non-virtual函数意味着接口和实现都必须被继承。
32:确定你的public继承塑模出is-a关系
最重要的规则:pulic继承意味is a。
比如A类继承B类,那么应该代表每一个类型为A的对象同时也使一个类型为B的对象。即B更一般化,A更特殊化。
总结
- public继承意味is-a。适用于base classes身上的每一件事情一定也适用于继承类身上,因为每一个derived class对象也都是一个base class对象。
33:避免遮掩继承而来的名称
继承关系的作用域是:派生类作用域被嵌套在基类作用域内。也就是说如果有相同的函数名就会覆盖掉基类。
可以使用using来使基类被覆盖部分可见。
例子
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();
}
//调用
Derived d;
int x;
d.mf1();
d.mf1(x);//错误,因为覆盖了
d.mf2();
d.mf3();
d.mf3(x);//错误,因为覆盖了
//解决办法
class Derived:public Base{
public:
using Base::mf1;
using Base::mf3;
virtual void mf1();
void mf3();
void mf4();
}
d.mf1(x);//现在可以了
d.mf3(x);//
总结
- derived classes内的名称会遮掩base classes内的名称,在Public继承下从来没有人希望如此。
- 为了让被遮掩的名称再见天日,可使用using声明式或转交函数。
转交函数即
virtual void mf1()
{
Base::mf1();
}
34:区分接口继承和实现继承
有时你希望派生类只继承成员函数的接口(也就是声明);有时候你又希望同时继承接口和实现,但又希望能够覆写继承的实现,有时又不允许覆写。
纯虚函数
virtual void draw() const=0;
声明一个pure virtual函数的目的是为了让derived classes只继承函数接口
非纯虚函数的目的是让derived classes继承该函数的接口和缺省实现
virtual void error(const std::string& msg);
如果成员函数是个Non-virtual函数,意味是它并不打算在derived classes中有不同的行为。也就是强制实现。
总结
- pure virtual函数只具体指定接口继承
- 普通virtual函数具体指定接口继承及缺省实现继承
- non-virtual函数具体指定接口继承以及强制性实现继承。
35:考虑virtual函数以外的其他选择
Non-Virtual Interface手法实现template method模式:即通过public非虚函数间接调用private 虚函数。
- 将机能从成员函数移到class外部函数,带来的一个缺点是,非成员函数无法访问class的non-public成员。
- tr1::function对象的行为就像一般函数指针。
- virtual函数的替代方案包括NVI手法及strategy设计模式的多种形式。NVI手法之声是一个特殊形式的template method设计模式。
36:绝不重新定义继承而来的non-virtual函数
即不要覆盖父类的普通函数。
37:绝不重新定义继承而来的缺省参数值
讨论的继承一个带有缺省参数值的virtual函数
virtual函数是动态绑定,缺省参数值是静态绑定。
38:通过复合塑模出has-a或根据某物实现出
复合例子
class Address{};
class PhoneNumber{};
class Person{
public:
....
private:
std::string name;
Address address;
PhoneNumber number;
}
- 复合的意义和Public继承完全不同
- 在应用域,复合意味has-a(有一个)
- 在实现域,符合意味is-implemented-in-terms-of(根据某物实现出)
39:明智而审慎地使用private继承
40:明智而审慎地使用多重继承
- virtual继承会增加大小,速度,初始化(及赋值)复杂度等等成本,如果virtual base classes不带任何数据,将是最具实用价值的情况
- 多重继承比单一继承复杂,它可能导致新的歧义性,以及对virtual继承的需要。
- 多重继承的确有正当用途,其中一个情节涉及public继承某个interface class和private继承某个协助实现的class的两相结合。