多重继承
多重继承也成MI,描述的是有多个直接基类的类。和单继承一样,公有MI表示的也是is-a关系。必须使用关键字public限定每一个基类(除了非特别之处,编译器默认为私有派生)
具有相同祖先的普通基类继承
SingerWaiter类继承自Singer和Waiter,而Singer和Waiter有这共同的祖先Worker。
- 包含基类组件worker的个数
在结构中,Singer和Waiter各自包含了一个Worker组件,所以在SingerWaiter对象中包含了两个Worker - 存在问题:
· 二义性:
Worker *P = &SingerWaiterObj;
这时候P不知道指向的是Singer包含的worker还是Waiter包含的worker组件,有两个地址可以选择,因此产生二义性。
· 二义性解决方法: 使用类型转换明确的指定对象
Worker *pw1 = (Waiter *) &SingerWaiterObj;
worker *pw1 = (Singer *) &SingerWaiterObj;
· 包含两个Worker对象拷贝还会导致其他问题
具有相同祖先的虚基类继承
虚基类使得从基类相同的多个类中派生出来的对象只继承一个基类对象。
虚基类声明
virtual关键字放在’:’ 后,基类名称前。可以在public前也可以在public后
class Singer: virtual public Worker{...};
or
class Singer: public virtual Worker{...};
所以SingerWaiter可以这么定义
class Singer: virtual public Worker{...};
class Waiter: virtual public Worker{...};
class SingerWaiter: public Singer, public Waiter{...};
关于虚基类
- 为什么使用术语虚
使用virtual(类似于关键字重载)不定义新关键字:防止新关键字与项目中的变量或者函数名重复,导致项目难以维护。 - 为什么不抛弃将基类声明为虚的这种方式,而使用虚行为成为多重继承的准则呢
· ① 某些情况下需要基类的多个拷贝
· ② 如果基类默认为虚的话,程序需要完成额外的计算。因此需要用到虚基类再声明是一种明智的选择 - 是否存在麻烦
· 为了使得虚基类能够工作,需要调整c++规则,并以不同形式编写一些代码;使用虚基类还可能需要修改已有代码。
虚基类多重继承构造函数规则
在包含虚基类的多重继承中,基类和派生类之间的信息自动传递就不起作用了。c++在基类是虚基类时,禁止信息通过中间类自动传递给基类。
旧构造规则
构造基类对象时,直接在派生类的初始值列表中调用继承基类的构造函数即可。因为信息可以通过中间类自动传递给基类。通过这种传递机制就可以依次正确的构造所需要的对象。
class A
{
int Ia;
public:
A(int a):Ia(a){};
...
};
class B:public A
{
...
int Ib;
public:
B(int b,int a):A(a),Ib(b){}
};
class C:public B
{
...
int Ic;
public:
C(int c,int b,int a):B(b,a),Ic(c){}
};
新构造函数规则
因为c++在基类是虚基类时,禁止信息通过中间类自动传递给基类。所以构造基类时候需要新的规则。
如已有的SingerWaiter类,假设其构造函数如下:
SingerWaiter(const Worker &wk, int p=0, int v = Singer::other)
: Worker(wk),Waiter(wk,p),Singer(wk,v){}
- Waiter(wk,p), Singer(wk,v) 无法将wk的信息传递给基类,因此这两个构造函数只能构造Waiter和Singer派生类部分的成员。所以传递的wk信息在这里没有作用。
- 显式的调用虚基类的构造函数Worker(const Worker &wk)构造虚基类。如果不显式调用,将使用默认Worker的构造函数。