C++的虚继承主要解决了数据冗余与二义性的问题,起实现方法是什么呢,我们先看一段代码。

#include<iostream>
using namespace std;
class A
{
public:
	int _num;
};
class B1 : public A
{

};
class B2 : public A
{

};
class C : public B1, public B2
{

};
int main()
{
	C c;
	cout << sizeof(c) << endl;
	c.B1::_num = 1;
	c.B2::_num = 2;
	cout << "c.B1::_num:" << c.B1::_num << endl;
	cout << "c.B2::_num:" << c.B2::_num << endl;
	cout << "c._num:" << c._num << endl;
	return 0;
}

    这是一个菱形继承,直接访问两个域中的_num并赋不同值时,两个域中的_num不会是同一个数值,证明了c中含有两个_num成员变量。当我们访问C域的_num,系统无法识别到底是访问B1所继承下来的还是B2所被继承下来的,这里编译器会报错,这里c的大小为8个字节,调试点开监视窗口我们可以看到:

wKioL1bi3c3h3lZ1AAAWWUq4Ptk376.png

一个C在不同域里继承了两份A类的成员变量,这样就造成了数据冗余及二义性。

    如何解决这个问题呢,C++中引入了关键字“vritual”在一代继承时加入关键字,修改后我们的代码为:

#include<iostream>
using namespace std;
class A
{
public:
	int _num;
};
class B1 : virtual public A
{

};
class B2 : virtual public A
{

};
class C : public B1, public B2
{

};
int main()
{
	C c;
	cout << sizeof(c) << endl;
	c.B1::_num = 1;
	c.B2::_num = 2;
	cout << "c.B1::_num:" << c.B1::_num << endl;
	cout << "c.B2::_num:" << c.B2::_num << endl;
	cout << "c._num:" << c._num << endl;
	return 0;
}

    这时我们发现通过c我们不但可以访问到自己域的成员变量,还可以访问到所继承类的成员变量,并且,当给不同域的同名成员变量赋值之后,它们的外在表现都是同一个值,这样节解决了二义性与数据冗杂,为什么说是外在表现呢,当我们打开调试窗口与内存时我们会看到:

调试窗口:

wKioL1bi4d-AeIpgAAAZelOlvMM218.png

调试窗口中,c不但包含两个父类的成员变量,还包含两个父类共同的父类的成员变量,这时c的大小为12。更惊奇的是,无论我们通过那个域名对其成员变量进行赋值,其他域的成员变量的值也会随之改变,这时为什么呢。这里我们打开内存监视窗口:

wKioL1bi5DKSS83JAACtWgubVrM129.png

这里我们可以看到,c中存的并不是真正的三个类中的成员变量,而是两个指针,与一个成员变量,拿着两个指针究竟指向的是什么呢,我们再打开更多的内存窗口进行观察:

第一个指针:

wKioL1bi5Sfy0Dy3AACVeme6o88788.png

第二个指针:

wKiom1bi5MXjDN9jAACZCMQ75lc791.png

    我们可以发现,这两个指针都为空,但是,在这两个指针底下,却有两个数字,这两个数字又恰好与存放这两个地址内容所在地址到存放c成员变量所在地址的偏移相同。由此可以看出VS下实现virtual关键字解决菱形继承的二义性与数据冗余的方法是,通过指针的偏移,使c中所存在的三块空间,在直接或间接的条件下,指向同一块空间。

    未完待续