C++中對象的內存佈局(一)

在上篇文章《戳我》中,簡單說了繼承的三種分類:單繼承、多重繼承、重複繼承。

一般的如果沒有虛函數,那對象的內存佈局就如我們看到的一樣,定義了幾個變量根據字節對齊就能算出其在內存中所佔字節大小。但是有了虛函數,就不同了,因爲有了虛函數就意味着存在虛函數指針,那指針我們知道是佔四個字節(32位)的,所以從本篇文章開始來分析存在虛函數時對象的內存佈局是怎麼樣的?(版本是Visual Studio2012)

對於單繼承:


寫出以下代碼:

class Father
{
public:
	Father(int data1):fa(data1){}
	virtual void f(){cout<<"Father::f()"<<" ";}
	virtual void ff(){cout<<"Father::ff()"<<" ";}
	virtual void fff(){cout<<"Father::fff()"<<" ";}
protected:
	int fa;
};

class Son:public Father
{
public:
	Son(int data):sa(data),Father(data){}
	virtual void f(){cout<<"Son::f()"<<" ";}
	virtual void ss(){cout<<"Son::ss()"<<" ";}
	virtual void sss(){cout<<"Son::sss()"<<" ";}
protected:
	int sa;
};
class Grandson:public Son
{
public:
	Grandson(int data):ga(data),Son(data){}
	virtual void f(){cout<<"Grandson::f()"<<" ";}
	virtual void ss(){cout<<"Grandson::ss()"<<" ";}
	virtual void ggg(){cout<<"Grandson::ggg()"<<" ";}
protected:
	int ga;
};
typedef void (*Function)(void);//函數指針
int main()
{
	//main函數主要進行打印
	Grandson *pgs=new Grandson(12);
	Function pFun=NULL;//定義一個函數指針

	cout<<"[0]   _vfptr"<<endl;
	for(int i=0;i<6;++i)
	{
		pFun=((Function*)(long**)(*(long*)(pgs)))[i];
		cout<<"   "<<"["<<i<<"]"<<" ";
		pFun();
		cout<<((long**)(*(long*)pgs))[i]<<endl;
	}

	cout<<"[1] "<<((long*)pgs)[1]<<endl;
	cout<<"[2] "<<((long*)pgs)[2]<<endl;
	cout<<"[3] "<<((long*)pgs)[3]<<endl;
	
	delete pgs;
	return 0;
}
注:
(long*)(pgs):將pgs強轉成long*
(*(long*)(pgs)):解引用,虛函數表的地址
(long**)(*(long*)(pgs)):虛函數表的地址強轉成二級指針

查看Grandson類生成對象的內存佈局:(點擊【項目】-》【屬性】-》【C/C++】-》【命令行】-》在其它選項處寫上

【/d1 reportSingleClassLayoutGrandson】-》點擊【確定】就好了 )


可以看出:

1、子類繼承了父類的成員變量,成員函數不佔空間。

2、有虛函數時出現了一個vfptr指針,並把該指針放在了內存的開始處(相對對象偏移爲0)。

3、同名的函數在虛表中被覆蓋。

4、同時也看到了有一張虛表的存在,虛表中從0號下標開始就存放的是虛函數,有一個問題就是從虛表中也能看出有RTTI信息(&Grandson_meta)和vfptr相對於類型的偏移信息(0),但它不在0號下標鎖對應的位置,那它在哪?怎麼打印出來?看到的寶寶希望能幫我解答。

將上面程序進行打印及調試,可以看到:


現在,我們來畫出單繼承下Grandson類的內存佈局:


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章