C++多继承中的内存布局
● 一个对象,如果它的类有多个基类则有多个虚函数表指针
● 在多继承中,对应各个基类的vptr按继承顺序依次放置在类的内存空间中,且子类与第一个基类共用一个vptr(第二个基类有自己的vptr)
先看一个例子:
class A1
{
public:
virtual void funcA1(){}
virtual void funcA11(){}
public:
int a1;
};
class A2
{
public:
virtual void funcA2(){}
virtual void funcA22(){}
public:
int a2;
};
class B : public A1 ,public A2
{
public:
virtual void funcA1(){}
virtual void funcA2(){}
virtual void funcB(){}
public:
int b;
};
在此情况下,类B的内存结构为:
我们来验证一下:
typedef void(**vptr)();
int main()
{
B* pb = new B; //值等于vptr1
A1* a1 = pb; //值等于vptr1
A2* a2 = pb; //值等于vptr2
return 0;
}
先看看这三个指针都指向了什么内容:
可以看到三个指针都指向了一个虚函数表,由上图知0x400c30偏移了B的虚函数表16个字节,因此B的虚函数表的地址为0x400c20,我们接下来从这个地址开始依次打印出B的虚函数中的内容:
这意味着:
Address | Value | Meaning |
0x400c20 | 0 | top_offset |
0x400c28 | 0x400ce0 | typeinfo for B |
0x400c30 | 0x400a34 | B::funcA1 |
0x400c38 | 0x4009da | A1::funcA11 |
0x400c40 | 0x400a52 | B::funcA2 |
0x400c48 | 0x400a76 | B::funcB |
0x400c50 | -16 | top_offset |
0x400c58 | 0x400ce0 | typeinfo for B |
0x400c60 | 0x400a6f | non-virtual thunk to B::funcA2() |
0x400c68 | 0x400a16 | A2::funcA22 |
上述内容已经验证了是存在两个虚表的,A2的虚表中funcA2并不直接指向B::funcA2(), 而是指向一个thunk:
通过汇编代码可以看到,首先是将this指针偏移了0x10 (16, 即top_offset) 个字节,再跳转到B::funcA2()。
thunk的意义在于虚函数实际上是要传入this指针的,我们要调用子类当中的实现那么必须先将父类指针转化为子类指针,这通过一次this指针偏移就可以完成。
我们再看funcA22:
可以看到funcA22是直接指向A2::funcA22()的.
#面经##cpp##面试##八股##面试题刺客退退退#