含有虚函数菱形的虚拟继承(没有对虚函数进行重写)

在VS2013编程,调试

问题 :  菱形继承会引来,二义性


1.源代码

</pre><pre name="code" class="cpp">
<span style="font-family: Arial, Helvetica, sans-serif;">#include <iostream></span>
using namespace std;

class Base


{
public:
	virtual void  FunTest()
	{
		cout << "Base::FunTest () " << endl;
	}
	virtual void  FunTest1()
	{
		cout << "Base::FunTest1 () " << endl;
	}
};

class C1 :virtual public Base
{
public:
	virtual void  FunTest2()
	{
		cout << "C1::FunTest2 () " << endl;
	}
};

class C2 :virtual public Base
{
public:
	virtual void  FunTest3()
	{
		cout << "C2::FunTest3 () " << endl;
	}
};

class Der : public C1, public C2
{
public:
	virtual void  FunTest4()
	{
		cout << "Der::FunTest4 () " << endl;
	}
	virtual void  FunTest5()
	{
		cout << "Der::FunTest5 () " << endl;
	}
	virtual void  FunTest6()
	{
		cout << "Der::FunTest6 () " << endl;
	}
};

typedef void(*vftab) ();

void Test()
{
	Der  d;
	cout << sizeof(d) << endl;
	cout << "-------C1---- " << endl;
	int *vfpt = (int *)(*(int *)&d);
	vftab vft = (vftab)(*(int *)vfpt);

	while (vft != 0)
	{
		vft();
		vfpt++;
		vft = (vftab)(*vfpt);
	}
	cout << endl;

	cout << "-------C2---- " << endl;
	vfpt = (int *)(*((int *)&d + 2));
	vft = (vftab)(*(int *)vfpt);

	while (vft != 0)
	{
		vft();
		vfpt++;
		vft = (vftab)(*vfpt);
	}
	cout << endl;

	cout << "-------Base---- " << endl;
	vfpt = (int *)(*((int *)&d + 4));
	vft = (vftab)(*(int *)vfpt);


	while (vft != 0)
	{
		vft();
		vfpt++;
		vft = (vftab)(*vfpt);
	}

}

int main()
{
	Test();

	getchar();
	return 0;
}

运行结果:

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第1张图片

这些结果是怎么来的哪?为什么 d的大小为20个字节??

接下来就看看d的内存吧

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第2张图片

现在知道了为什么d的大小为20个字节了吧!但是问题有来了,d的内存中存的都是些什么东西哪?

一步一步看吧!

貌似这些都是地址,那就看看这些地址有存了些什么

一.  看一下到 地址(0x 00 2a dd  2c)


可以从监视1看到 地址(0x 00 2a 11 59)是虚函数C1::FunTest2()的入口地址



可以从监视1看到 地址(0x 00 2a 12 53)是虚函数Der::FunTest4()的入口地址


含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第3张图片

可以从监视1看到 地址(0x 00 2a 13 de)是虚函数Der::FunTest5()的入口地址


含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第4张图片

可以从监视1看到 地址(0x 00 2a 10 05)是虚函数Der::FunTest6()的入口地址


总结1:地址(0x 00 2a dd  2c),应该是类Der从类C1继承下来虚表的地址,虚表中存的是类Der和类C1的虚函数的地址


二.  看一下到 地址(0x 00 2a dd  5c)

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第5张图片


可以看到  0x 00 4e f9 30  + 0x 00 00 00 0c  = 0x 00 4e f9 3c

 总结2:地址(0x 00 2a dd  5c)下,应该是存的是偏移


三.    看一下到 地址(0x 00 2a dd  44)


可以从监视1看到 地址(0x 00 2a 10 69)是虚函数C2::FunTest3()的入口地址


 总结3:地址(0x 00 2a dd  44),应该是类Der从类C2继承下来虚表的地址,虚表中存的是类C2的虚函数的地址


四.   看一下到 地址(0x 00 2a dd  68)


含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第6张图片


可以看到  0x 00 4e f9 38  + 0x 00 00 00 04  = 0x 00 4e f9 3c

 总结4:地址(0x 00 2a dd  68)下,应该是存的是偏移


五.看一下到 地址(0x 00 2a dd  50)



可以从监视1看到 地址(0x 00 2a 12 c6) 是虚函数Base::FunTest()的入口地址


含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第7张图片


可以从监视1看到 地址(0x 00 2a 12 7b) 是虚函数Base::FunTest1()的入口地址


 总结5:地址(0x 00 2a dd  50),应该是类Der从类Base继承下来虚表的地址,虚表中存的是类Base的虚函数的地址


根据上面的一步一步的分析 :可以得到菱形虚拟继承(含有虚函数,但没有被重写)的模型:

含有虚函数菱形的虚拟继承(没有对虚函数进行重写)_第8张图片

看看这两个的偏移,就是它们保证了类 Der在继承 类Base的虚拟函数,以及类Base的数据成员的唯一性,从而避免了二义性的产生。

注意:在函数Test()中的地址偏移,是为了方便从内存中取得地址,查看里面是什么内容,如果每个类加上自己的数据成员,那就不是那样取值了,

             我说的是什么哪 ?意思就是说 :

            vfpt = (int *)(*((int *)&d + 2));

   vfpt = (int *)(*((int *)&d + 4));

          看见 2 和 4 了吧,说的就是这个

你可能感兴趣的:(含有虚函数菱形的虚拟继承(没有对虚函数进行重写))