天天看點

4.7多重繼承資料布局與this調整深談單一繼承資料成員布局this指針偏移知識補充多重繼承且父類都帶虛函數的資料成員布局

單一繼承資料成員布局this指針偏移知識補充

this指針,加上偏移值 就的能夠通路對應的成員變量,比如m_bi = this指針+偏移值

#include <iostream>
using namespace std;

class Base
{
public:
	int m_bi;
	Base()
	{
		printf("Base::Base()的this指針是:%p!\n", this);//列印父類this指針
	};
};
class MYACLS :public Base
{
public:
	int m_i;
	int m_j;

	virtual void myvirfunc() {} //虛函數
	MYACLS()
	{
		printf("MYACLS::MYACLS()的this指針是:%p!\n", this);//列印子類this指針
		
	}
	~MYACLS()
	{
		
	}
};

int main()
{
	cout << sizeof(MYACLS) << endl;//24
	//偏移值(相對父類或者子類this指針的)
	printf("MYACLS::m_bi = %d\n", &MYACLS::m_bi);//0
	printf("MYACLS::m_i = %d\n", &MYACLS::m_i);//12
	printf("MYACLS::m_j = %d\n", &MYACLS::m_j);//16

	MYACLS obj;

	/*

	Base::Base()的this指針是:0000006F4AEFFA40!
	MYACLS::MYACLS()的this指針是:0000006F4AEFFA38!
	this指針差了8位元組,差的是個vptr

	*/
	return 1;
}

           

多重繼承且父類都帶虛函數的資料成員布局

#include <iostream>
using namespace std;

class Base1
{
public:
	int m_bi;
	virtual void mybvirfunc() {}

	Base1()
	{
		printf("Base1::Base1()的this指針是:%p!\n", this);
	}
};
class Base2
{
public:
	int m_b2i;
	virtual void mybvirfunc2() {}

	Base2()
	{
		printf("Base2::Base2()的this指針是:%p!\n", this);
	}
};
class MYACLS :public Base1, public Base2
{
public:
	int m_i;
	int m_j;

	virtual void myvirfunc() {} //虛函數
	MYACLS()
	{
		int abc = 1; //友善加斷點
		printf("MYACLS::MYACLS()的this指針是:%p!\n", this);
	}
	~MYACLS()
	{
		int def = 0;//友善加斷點
	}
};
int main()
{
	//二:多重繼承且父類都帶虛函數的資料成員布局
	//(1)通過this指針列印,我們看到通路Base1成員不用跳 ,通路Base2成員要this指針要偏移(跳過)16位元組;
	//(2)我們看到偏移值,m_bi和m_b2i偏移都是8;
	//(3)this指針,加上偏移值 就的能夠通路對應的成員變量,比如m_b2i = this指針+偏移值

	//我們學習得到一個結論:
	//我們要通路一個類對象中的成員,成員的定位是通過:this指針(編譯器會自動調整)以及該成員的偏移值,這兩個因素來定義;
	   //這種this指針偏移的調整 都需要編譯器介入來處理完成;
	cout << sizeof(MYACLS) << endl;//40
	printf("MYACLS::m_bi = %d\n", &MYACLS::m_bi);//8
	printf("MYACLS::m_b2i = %d\n", &MYACLS::m_b2i);//8
	printf("MYACLS::m_i = %d\n", &MYACLS::m_i);//32
	printf("MYACLS::m_j = %d\n", &MYACLS::m_j);//36

	MYACLS myobj;
	myobj.m_i = 3;
	myobj.m_j = 6;
	myobj.m_bi = 9;
	myobj.m_b2i = 12;

	/*
	Base1::Base1()的this指針是:00000038C818F468!
	Base2::Base2()的this指針是:00000038C818F478!
	MYACLS::MYACLS()的this指針是:00000038C818F468!

	*/
	
	return 1;
}

           

左到右:

子類對象位址(紅色)

Base1繼承來的vptr,補齊,m_bi(藍色)

Base2繼承來的vptr, m_b21, 補齊(黃色)

子類自己的m_i和m_j(紅色)

4.7多重繼承資料布局與this調整深談單一繼承資料成員布局this指針偏移知識補充多重繼承且父類都帶虛函數的資料成員布局

編譯器角度

父類指針指向子類對象時,this指針偏移問題

int main()
{
	//cout << sizeof(MYACLS) << endl;//40
	//printf("MYACLS::m_bi = %d\n", &MYACLS::m_bi);//8
	//printf("MYACLS::m_b2i = %d\n", &MYACLS::m_b2i);//8
	//printf("MYACLS::m_i = %d\n", &MYACLS::m_i);//32
	//printf("MYACLS::m_j = %d\n", &MYACLS::m_j);//36

	MYACLS myobj;
	
	Base2 *pbase2 = &myobj; //this指針調整導緻pbase2實際是向前走16個位元組的記憶體位置的
	                       
	//站在編譯器視角,把上邊這行語句進行了調整

	//Base2 *pbase2 = (Base2 *)(((char *)&myobj) + sizeof(Base1));


	Base1 *pbase1 = &myobj; //不用調整this指針

	Base2 *pbase3 = new MYACLS(); //父類指針指向new的子類對象 ,這裡new出來的是40位元組
	MYACLS *psubobj = (MYACLS *)pbase3; //比上邊位址小了16位元組(偏移)
	//delete pbase3; //報異常。是以我們認為pbase2裡邊傳回的位址不是配置設定的首位址,而是偏移後位址。
	//          //而真正配置設定的首位址應該是在psubobj裡邊的這個位址
	delete psubobj;


	return 1;
}

           

繼續閱讀