菱形继承
图1
即B、C继承A,D继承B、C,简单代码实现如下:
#pragma once
#include<iostream>
using namespace std;
class A
{
public:
void Display()
{
cout << a << endl;
}
private:
int a;
};
class B :public A
{
private:
int b;
};
class C :public A
{
int c;
};
class D :public B, public C
{
private:
int d;
};
int main()
{
D dd;
return 0;
}
给D创建一个对象dd,通过监视窗口可以看到对象dd中的成员,如下图所示:
图2
由上图可知,类D创建的对象dd中有两组A的成员变量,在调用时会出现“二义性”的问题,main函数代码如下:
int main()
{
D dd;
dd.a = 10;
dd.Display();
return 0;
}
运行结果如下图所示:
图3
若要正确调用,需显示指定访问哪个父类的成员,代码如下:
int main()
{
D dd;
//dd.B::a = 10;此时不可调用成员变量a,因为a是私有成员
dd.C::Display();
return 0;
}
但是此种方法通常不被采用。
虚继承
作用:解决菱形继承中二义性和数据冗余的问题。
代码如下:
#pragma once
#pragma once
#include<iostream>
using namespace std;
class A
{
public:
void Display()
{
cout << a << endl;
}
private:
int a;
};
class B :virtual public A
{
private:
int b;
};
class C :virtual public A
{
private:
int c;
};
class D :public B, public C
{
private:
int d;
};
int main()
{
A aa;
B bb;
C cc;
D dd;
return 0;
}
打开监视窗口:
图4
给dd中的每个成员变量赋值:
int main()
{
D dd;
dd.a = 1;
dd.b = 2;
dd.c = 3;
dd.d = 4;
return 0;
}
此时,对程序进行调试,察看内存窗口:
图5
由内存窗口可知,A的成员变量在对象dd中只有一份,由此解决了数据冗余的问题。
图6
在上图中,B和C在对象dd的内存中除了存放自己的成员变量之外,还有一串类似地址的数据,不妨在内存中试一试,输入后的结果如下图:
图7
将图6与图7对比,可得到图8:
图8
经过观察,可知14 00 00 00代表的是十进制数20,0c 00 00 00代表的是十进制数12,而在图6中,从B起始地址到A的起始地址之间的偏移量为20,从C起始地址到A的起始地址之间的偏移量为12,称图7中的两个表为虚机表,通过虚机表中的偏移量可以解决数据的二义性问题。
总结:
1.虚继承解决了在菱形继承体系中子类对象包含多份父类对象的数据冗余和浪费空间资源的问题。
2.一般不定义菱形结构的虚继承体系结构,因为使用虚继承也带来了性能上的损耗。