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##面试##八股##面试题刺客退退退#