- 作者: 雪山肥鱼
- 时间:20210901 00:19
- 目的: 多重继承成员布局与this指针的调整
父类无虚函数,子类有虚函数
class Base
{
public:
int m_bi;
Base() {
printf("Base::Base()的this 指针是 %p\n", this);
}
};
class Myclass : public Base
{
public:
int m_i;
int m_j;
virtual void myvirfunc()
{}
Myclass()
{
int abc = 1;
printf("Myclass::Myclass()的this 指针是 %p\n", this);
}
~Myclass()
{
int def = 0;
}
};
int main(int argc, char **argv)
{
printf("myclass:m_bi = %d\n", &Myclass::m_bi);
printf("myclass:m_i = %d\n", &Myclass::m_i);
printf("myclass:m_j = %d\n", &Myclass::m_j);
Myclass myobj;
printf("myobj的地址是:%p\n", &myobj);
myobj.m_bi = 0;
myobj.m_i = 1;
myobj.m_i = 2;
return 0;
}
this指针位置不同.png
上述布局的输出结果很明显,如下图所示:
布局.png
- 访问父类的this 与 子类的 this 不同
- 访问成员变量,this指针都需要进行调整, 才能访问父类的东东。
子类有虚函数,父类也有虚函数
class Base
{
public:
int m_bi;
virtual void voidfatherfun()
{
}
Base() {
printf("Base::Base()的this 指针是 %p\n", this);
}
};
class Myclass : public Base
{
public:
int m_i;
int m_j;
virtual void myvirfunc()
{}
Myclass()
{
int abc = 1;
printf("Myclass::Myclass()的this 指针是 %p\n", this);
}
~Myclass()
{
int def = 0;
}
};
int main(int argc, char **argv)
{
printf("myclass:m_bi = %d\n", &Myclass::m_bi);
printf("myclass:m_i = %d\n", &Myclass::m_i);
printf("myclass:m_j = %d\n", &Myclass::m_j);
Myclass myobj;
printf("myobj的地址是:%p\n", &myobj);
myobj.m_bi = 0;
myobj.m_i = 1;
myobj.m_i = 2;
return 0;
图片.png
- this 指针相同,父类与子类即使虚函数不同也共用一个vptr
- 访问父类的东东,this 指针不需要进行调整
多重继承且父类都带虚函数的数据成员布局
class Base1
{
public:
int m_b1i;
virtual void voidfatherfun()
{
}
Base1() {
printf("Base1::Base1()的this 指针是 %p\n", this);
}
};
class Base2
{
public:
int m_b2i;
virtual void voidfatherfun2()
{
}
Base2() {
printf("Base2::Base2()的this 指针是 %p\n", this);
}
};
class Myclass : public Base1, public Base2
{
public:
int m_i;
int m_j;
virtual void myvirfunc()
{}
Myclass()
{
int abc = 1;
printf("Myclass::Myclass()的this 指针是 %p\n", this);
}
~Myclass()
{
int def = 0;
}
};
int main(int argc, char **argv)
{
printf("sizeof Myclass:%d\n", sizeof(Myclass));
printf("myclass:m_bi = %d\n", &Myclass::m_b1i);
printf("myclass:m_bi = %d\n", &Myclass::m_b2i);
printf("myclass:m_i = %d\n", &Myclass::m_i);
printf("myclass:m_j = %d\n", &Myclass::m_j);
Myclass myobj;
printf("myobj的地址是:%p\n", &myobj);
return 0;
}
运行结果.png
B1 和 Myclass 共用一个 this 指针, 如果访问B2 则需要修改this 指针的值。
内存布局如下:
内存布局.png
注意 this 指针的位置是不同的!
结论:
我们要访问一个类对象中的成员,成员定位是通过this指针的来的(编译器会自动调整,比如我们讨论的多重继承中Base1, Base2 就不用) 以及偏移值来定位的。
this指针的调整需要编译器来介入。
深解多重继承中的this偏移
Myclass myobj;
myobj.m_i = 1;
myobj.m_j = 2;
myobj.m_b1i = 3;
myobj.m_b2i = 4;
Base2 * pBase2 = &myobj;
这里的现象很有意思
&myobj 的地址 :
0x00bdfe04 {m_i=1 m_j=2 },
pbase2 的地址:
pbase2 = 0x00bdfe0c
两者并不相同,差了8个字节。
实际编译器是这么操作的:
Base2 * pbase2 = ( ( (char*)&myobj) + sizeof(Base1) );
这里做的,个人认为是调整了this指针的位置。
同理
Base1 * pbase1 = &myobj 就不会进行调整
Base2 *pBase3 = new Myclass(); //指向的是 Base2 的 this 指针位置 428
Myclass *psubobj = (Myclass*)pBase3;// 420
分配堆后,进行转化,发现this 指针 同样调整了8个字节。多继承完全阐述了 编译器堆this指针的调整。
delete psubobj; // 正确的
//delete pBase3;// 分配的有问题,说明pBase3 并不是 真个堆的首地址。而是调整了this指针后的地址
下面的 delete pBase3 是绝对有问题的。因为delete不干净。会报错呢。
再次举例:
多重继承的举例.png
学到: 多重继承中,this指针的调整
网友评论