10.4.2 解决方案

    使用关键字virtual将共同基类A声明为虚基类,可有效解决上述问题。在定义由共同基类直接派生的类(代码10.3中的类B和类C)时,使用下列格式进行定义。


    class派生类名:virtual派生方式基类名 { //类定义 };

    这称为由基类“虚拟派生”派生类,某个基类的所有虚拟派生类只维护基类成员的一个版本,据此对代码10.3进行修改,如代码10.4所示。

    代码10.4 虚基派生VirtualBase


    <——————————-文件名:example1004.cpp————————————————> 01 #include<iostream> 02 using namespace std; 03 class A//公共虚基类A 04 { 05 protected://protected成员列表 06 int x; 07 public: 08 A(int xp=0)//构造函数,带默认构造参数 09 { 10 x=xp; 11 } 12 void SetX(int xp)//SetX函数用以设置protected成员x 13 { 14 x=xp; 15 } 16 void print()//print函数输出信息 17 { 18 cout<<"this is x in A:"<<x<<endl; 19 } 20 }; 21 class B:virtual public A//类B由类A虚基派生而来 22 { 23 }; 24 class C:virtual public A//类C由类A虚基派生而来 25 { 26 }; 27 class D:public B,public C//类D由类B和类C共同派生 28 { 29 }; 30 int main() 31 { 32 D exD;//声明一个类D对象exD 33 exD. SetX(5);//SetX函数,由virtual派生,在类D中只有一个版本,不会二义 34 exD. print();//print函数,由virtual派生,在类D中只有一个版本,不会二义 35 exD. B:print();//还可用类名显式说明调用函数的版本 36 exD. C:print(); 37 exD. A:print(); 38 return 0; 39 }

    输出结果如下所示。


    this is x in A:5 this is x in A:5 this is x in A:5 this is x in A:5

    【代码解析】代码第21和24行,通过对类B和类C进行虚基派生,成功解决了二义性问题,虚基派生时,即使类D是由类B和类C多基派生而来的,类D中也只有一份A对象的复制,如图10.6所示。

    10.4.2 解决方案 - 图1

    图 10.6 虚基派生DAG示意图

    说明

    代码10.4 中,使用exD.print()、exD.B:print()、exD.C:print()和exD.A:print()是完全等价的,而且不必考虑访问权限和可见性,exD.x、exD.B.x、exD.C.x和exD.A.x对应着同一块内存单元。

    关于虚基派生有以下两点需要特别说明。

    ❑关键字virtual和派生方式(public、private以及protected)的先后顺序无关。

    ❑为保证共同基类的成员在派生类中只有一个备份,必须将共同基类的直接派生类(代码10.4中为类B和类C)都定义为virtual方式,否则,派生类从所有virtual派生的路径中只能得到共同基类成员的一个备份,此外,还会从其他每条非virtual派生路径中得到一个备份。