三 虚基类
如图所示,如果类B和类C均由类A派生而成,而类D又是由类B和类C共同派生而成,则在派生类D中就出现了基类A的两个拷贝,从而产生了冲突。为了解决此问题,VC++中引入了虚基类的概念,即在由类A派生类B和类C时,如果将基类A说明为虚基类,则类A在此后的派生类中只生成一个拷贝。例如:
#include<iostream.h>
class A{
public:
int a,b;
A(int x){a=x;}
};
class B:virtual public A{ //A行
public:
int b;
B(int x,int y):A(y){b=x;}
};
class C:public virtual A{ //B行
public:
int c;
C(int x,int y):A(y){c=x;}
};
class D:public B,public C{
public:
int d;
D(int x,int y,int z,int q):B(y,z),C(z,q),A(q) //C行
{d=x;}
};
void main()
{
D d1(1,2,3,4);
cout<<d1.a<<'\t'<<d1.b<<'\t'<<d1.c<<'\t'<<d1.d<<'\n';//A行
}
虚基类的说明使用关键字virtual,其基本格式如下:
class <派生类名>:virtual <派生特性> <基类名>
{…}
其中关键字virtual可以放在“派生特性”前(如上面的A行),也可以放在“派生特性”后(如上面的B行)。
由于类D的构造函数分别调用了类B和类C的构造函数,而类B和类C的构造函数又都分别调用了类A的构造函数,这样,在类D生成对象时就无法确定从类A中继承来的成员的初始值。VC++中规定,在这种情况下,在执行类B和类C的构造函数时均不调用类A的构造函数,而是由类D的构造函数来直接调用类A的构造函数。所以,如果类A没有缺省的构造函数,在类D的构造函数中必须明确类A的构造函数的调用。如果上面程序的C行中去掉最后的类A的构造函数调用A(q),即将C行改为如下形式:
D(int x,int y,int z,int q):B(y,z),C(z,q)
则系统编译时将报错。上面的程序运行后输出:
4 3 2 1
从程序的输出可以看出,类D中从类A继承来的成员a的初始化是由C行中的A(q)完成的。