单一继承数据成员布局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(红色)
编译器角度
父类指针指向子类对象时,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;
}