多重继承描述的是多个直接基类的类:
class SingingWaiter:public Waiter ,public Singer {...}
多重继承也带来了新的问题:1,多个基类继承同一类的多个对象。2,从两个基类继承的同名函数 。
在这里我们构建一个有共同祖先的多重继承。
class Wroker{
};
class Waiter:public Worker{
};
class Singer:public Worker
};
class SingingWaiter:public Waiter,public Singer{
};
问题一:因为Singer和Waiter 都继承一个Wroker子对象,所以SingingWaiter将包含两个Wroker子对象。
当我们将派生类对象地址赋给基类指针时,将发生二义性
SingingWaiter ed;
Worker * pw = &ed;
ed 包含两个Worker对象,那么问题来了,到底将哪一个地址赋给基类指针呢?
我们首先想到的就是限定它们类型,Worker * pw = (Waiter * )ed; Worer * pw = (Waiter *) ed;
是不是很low,有木有高大上的,yes,它就是“虚基类”。
它的作用就是从多个类(它们的基类相同)派生出的对象只继承一个基类对象。
上面的声明做一些改变,加入关键字virtual:
class Wroker{
};
class Waiter: virtual public Worker{
};
class Singer:virtual public Worker
};
class SingingWaiter:public Waiter,public Singer{
};
这样SingingWaiter对象只包含Worker对象的一个副本。
当然了,引入虚基类也是要付出代价的,改变原来的一些规则,如构造函数规则。
对于非虚基类,构造函数的初始化列表调用的是本类的基类构造函数
class A{
private:
int n;
public:
A(int n = 0):a(n) {}
};
class B: publlic A{
private :
int b;
public:
B(int m = 0,int n = 0): A(n),b(m){} //调用它的基类构造函数A()
};
class C:public B{
private:
int c;
public:
C(int q = 0,int m = 0,int n = 0) :B(m,n),c(q); // 调用它的基类构造函数B()
};
但是对于虚基类,这种信息自动传递将不起作用。
SingingWaiter(const Worker & wk,int p = 0,int v = 0):Waiter(wk,p),Singer(wk,v);
在传递信息时,将有两个途径将wk 传递给Worker对象,为了避免冲突,当基类为虚时,禁止信息通过中间类自动传递给基类。因此将显示调用Worker构造函数。
SingingWaiter(const Worker & wk,int p = 0,int v = 0):Worker(wk),Waiter(wk,p),Singer(wk,v);
问题二:同名函数的二义性
加入SingingWaiter 木有重新定义show(),当SingingWaiter 对象 sw调用sw.show()时,由于Waiter 和Singer中都定义了show()函数,将产生二义性。
当然,我么可以使用作用域运算符来限定意图,比如,sw.Singer::show()
然而更好的方法就是重新定义show(),对于单继承来说,让派生类调用基类的方法是可以的,但是对于多继承来说,又要出现问题了。
void Worker::show(){
}
void Waiter::show(){
Worker::show();
cout << "...." << endl;
}
void Singer::show(){
Worker::show();
cout << "...." <<endl;
}
SingingWaiter::show(){
Singer:show(); //
Waiter::show(); //两次调用Worker:show()
}
一种解决办法就是模块化方式,提供一个只显示Worker组件的方法,和只显示Waiter 或者Singer的组件,然后在Singingwaiter:;show()中将组件结合起来,这样就不会重复调用。
还有一种解决办法就是将所有数据组件都设置为保护的,而不是私有的,这样SingingWaiter 就可以访问到基类Worker的方法。
虚基类也将改变同名函数二义性的规则。
如果使用虚基类,而且函数名无类名限定,不一定导致二义性。以为存在优先级概念。
优先级:派生类中的名称优先于直接或间接祖先类中的同名函数。
class B{
public:
short q();
};
class C:virtual public B{
public:
long q();
};
类c中的q() 优先于B中的q()定义。
多重继承的复杂性主要来源于派生类通过多条途径继承同一个基类引起的。