当某类的部分或者全部直接基类是从另一个共同的基类派生而来时,在这些直接基类中从上一级共同基类继承来的成员就拥有相同的名称。在派生类的对象中,这些同名数据成员在内存中同时多个副本,同一个函数名会有多个映射。
可以通过作用域分辨符来唯一标识并分别访问它们,也可以**将共同基类设置为虚基类,这时,从不同的路径继承过来的同名数据成员在内存中就只有一个副本,同一个函数名也只有一个映射。**这样就解决了同名成员的唯一标识问题。
虚基类的声明是在派生类的定义过程中进行的。
1.声明虚基类的一般语法形式为:
class 派生类名::virtual 继承方式 基类名
上述语句声明基类为派生类的虚基类。在多继承情况下,虚基类关键字的作用范围和继承方式关键字相同,只对紧跟其后的基类起作用。
声明了虚基类之后,虚基类的成员在进一步派生过程中和派生类一起维护同一个内存数据副本。
【例】虚基类举例
一个基类B0,声明了数据成员v0和函数成员fun0,由B0公有派生了类B和类B2,在派生时声明B0为虚基类,再以B1,B2作为基类共同公有派生产生了新类D。在派生类中不再添加新的同名成员(如果有同名成员,同样遵循隐藏规则),这时的D类中,通过B1,B2两条派生路径继承来的基类B0中的成员v0和fun0只有一份副本,类的派生关系图及派生类的结构如下所示:
使用了虚基类之后,在派生类D中只有唯一的数据成员v0。在建立D类对象的模块中,直接使用“对象名.成员名”的方式就可以唯一标识和访问这些成员。
程序代码如下:
#include<iostream>
using namespace std;
class B0
{
public:
int v0;
void fun0()
{
cout << "基类B0的成员" << endl;
}
};
class B1 :virtual public B0
{
public:
int v1;
};
class B2 :virtual public B0
{
public:
int v2;
};
class D :public B1, public B2
{
public:
int v;
void fun()
{
cout << "派生类D的成员" << endl;
}
};
int main()
{
D d;
d.v0 = 2;
d.fun0();
return 0;
}
运行结果:
分析:
虚基类声明只是在类的派生过程中使用了virtual关键字。在主函数在,创建了一个派生类D的对象d,通过成员名称就可以访问该类的成员v0和fun0。
使用作用域分辨符和虚基类技术的比较:
使用作用域分辨符时,在派生类中拥有同名成员的多个副本,分别通过直接基类名来唯一标识,可以存放不同的数据、进行不同的操作;使用虚基类技术只维护一份成员副本。相比之下,使用作用域分辨符可以容纳更多的数据,使用虚基类技术更为简洁,内存空间更为节省。