C++虚拟继承下对象的内存布局:GCC实现

《深入探索C++对象模型》第3章中提到,一种实现虚拟继承的方法是:在virtual function table中放置virtual base class的offset,这样virtual function table可经由正值和负值来索引。如果是正值,索引到virtual functions;如果是负值,则索引到virtual base class offsets。GCC正是采用了类似的实现方式,我写了如下程序进行了测试:

  • 图1是4个类的继承关系图:其中Y、Z虚拟继承自X,A多重继承自Y和Z。

图 1:继承关系

#include <iostream>
using namespace std;

typedef void (*Func)();

class X {
public:
virtual void f1() { cout << "X::f1()" << endl; }
virtual void f2() { cout << "X::f2()" << endl; }
virtual void f3() { cout << "X::f3()" << endl; }
int i;
};

class Y: virtual public X {
public:
virtual void f1() { cout << "Y::f1()" << endl; }
virtual void g() { cout << "Y::g()" << endl; }
int j;
};

class Z: virtual public X {
public:
virtual void f2() { cout << "Z::f2()" << endl; }
virtual void h() { cout << "Z::h()" << endl; }
int k;
};

class A: public Y, public Z {
public:
virtual void t() { cout << "A::t()" << endl; }
int l;
};

int main()
{
A a;
cout << "addr a: " << &a << endl; // addr a: 0x7fff54b53b48

cout << "Y_Vptr: " << (Func *)&a << endl; // Y_Vptr: 0x7fff54b53b48
Func *Y_Vptr = (Func *)*((Func *)&a);
Func pf1 = *Y_Vptr;
Func pg = *(Y_Vptr + 1);
Func pt = *(Y_Vptr + 2);

cout << "addr j: " << &a.j << endl; // addr j: 0x7fff54b53b50

cout << "Z_Vptr: " << (Func *)&a + 2 << endl; // Z_Vptr: 0x7fff54b53b58
Func *Z_Vptr = (Func *)*((Func *)&a + 2);
Func pf2 = *Z_Vptr;
Func ph = *(Z_Vptr + 1);

cout << "addr k: " << &a.k << endl; // addr k: 0x7fff54b53b60
cout << "addr l: " << &a.l << endl; // addr l: 0x7fff54b53b64

cout << "X_Vptr: " << (Func *)&a + 4 << endl; // X_Vptr: 0x7fff54b53b68
Func *X_Vptr = (Func *)*((Func *)&a + 4);
Func x_pf1 = *X_Vptr;
Func x_pf2 = *(X_Vptr + 1);
Func pf3 = *(X_Vptr + 2);

cout << "addr i: " << &a.i << endl; // addr i: 0x7fff54b53b70

pf1(); // Y::f1()
pg(); // Y::g()
pt(); // A::t()

pf2(); // Z::f2()
ph(); // Z::h()

x_pf1(); // Y::f1()
x_pf2(); // Z::f2()
pf3(); // X::f3()
}

图 2:内存布局

  • 经过验证,一个A类对象的内存布局应如图2所示:其中蓝色部分我没有详细求证,参考自博文。此外有关虚函数表的内容,可以参考陈皓的博文。还要注意一点:C++ Standard并没有明确规定实现细节,VC等其他编译器可能有不同的实现方式。
0%