程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 含有虛函數菱形的虛擬繼承(沒有對虛函數進行重寫)

含有虛函數菱形的虛擬繼承(沒有對虛函數進行重寫)

編輯:C++入門知識

含有虛函數菱形的虛擬繼承(沒有對虛函數進行重寫)


在VS2013編程,調試

問題 : 菱形繼承會引來,二義性

1.源代碼


#include 
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;
}

運行結果:

\

這些結果是怎麼來的哪?為什麼d的大小為20個字節??

接下來就看看d的內存吧

\

現在知道了為什麼d的大小為20個字節了吧!但是問題有來了,d的內存中存的都是些什麼東西哪?

一步一步看吧!

貌似這些都是地址,那就看看這些地址有存了些什麼

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

\

可以從監視1看到 地址(0x 00 2a 11 59)是虛函數C1::FunTest2()的入口地址

\

可以從監視1看到 地址(0x 00 2a 12 53)是虛函數Der::FunTest4()的入口地址

\

可以從監視1看到 地址(0x 00 2a 13 de)是虛函數Der::FunTest5()的入口地址

\

可以從監視1看到 地址(0x 00 2a 10 05)是虛函數Der::FunTest6()的入口地址

總結1:地址(0x 00 2a dd 2c),應該是類Der從類C1繼承下來虛表的地址,虛表中存的是類Der和類C1的虛函數的地址

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

\

可以看到 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)

\

可以看到 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()的入口地址

\

可以從監視1看到 地址(0x 00 2a 12 7b) 是虛函數Base::FunTest1()的入口地址

總結5:地址(0x 00 2a dd 50),應該是類Der從類Base繼承下來虛表的地址,虛表中存的是類Base的虛函數的地址

根據上面的一步一步的分析 :可以得到菱形虛擬繼承(含有虛函數,但沒有被重寫)的模型:

\

看看這兩個的偏移,就是它們保證了類 Der在繼承 類Base的虛擬函數,以及類Base的數據成員的唯一性,從而避免了二義性的產生。

注意:在函數Test()中的地址偏移,是為了方便從內存中取得地址,查看裡面是什麼內容,如果每個類加上自己的數據成員,那就不是那樣取值了,

我說的是什麼哪 ?意思就是說 :

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

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

看見 2 和 4 了吧,說的就是這個

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved