天天看点

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

           

继续阅读