C++ 对象的内存布局(下)这篇文章的“单一虚拟继承”和“钻石型虚拟继承”时的类内存布局讲得不太清楚,我有一处疑问,我用的是VS2005,因此记录一下。
类继承图如下:
这里:类B被类B1和B2虚拟继承,而B1和B2同时被D继承。
B1的f()、B2的f()覆盖了B的f();
D的f()覆盖了B1的f(),D的f1()覆盖了B1的f1()
D的f()覆盖了B2的f(),D的f2()覆盖了B2的f2()
类代码如下:
class B
{
public:
int ib;
char cb;
public:
B():ib(0),cb('B') {}
virtual void f() { cout << "B::f()" << endl;}
virtual void Bf() { cout << "B::Bf()" << endl;}
};
class B1 : virtual public B//虚拟继承,钻石型继承
{
public:
int ib1;
char cb1;
public:
B1():ib1(11),cb1('1') {}
virtual void f() { cout << "B1::f()" << endl;}
virtual void f1() { cout << "B1::f1()" << endl;}
virtual void Bf1() { cout << "B1::Bf1()" << endl;}
};
class B2: virtual public B
{
public:
int ib2;
char cb2;
public:
B2():ib2(12),cb2('2') {}
virtual void f() { cout << "B2::f()" << endl;}
virtual void f2() { cout << "B2::f2()" << endl;}
virtual void Bf2() { cout << "B2::Bf2()" << endl;}
};
class D : public B1, public B2
{
public:
int id;
char cd;
public:
D():id(100),cd('D') {}
virtual void f() { cout << "D::f()" << endl;}
virtual void f1() { cout << "D::f1()" << endl;}
virtual void f2() { cout << "D::f2()" << endl;}
virtual void Df() { cout << "D::Df()" << endl;}
};
(1)单一虚拟继承:B1虚拟继承自B
查看类B1的内存布局的代码如下:
{
int** pVtab = NULL;
Fun pFun = NULL;
B1 bb1;
pVtab = (int**)&bb1;
cout << "sizeof(B1) = " << sizeof(B1) << endl;
cout << "[0] B1::_vfptr->" << endl;
for (int i = 0; ; i++)
{
pFun = (Fun)pVtab[0][i];
cout << '\t' << "[" << i << "] 0x" << (int*)pFun << "\t";
if (pFun == NULL)
{
cout << endl;
break;
}
pFun();
}
cout << "[1] B1::_vbptr->" << endl;
cout << "\t" << "[0] B1dB1bptrB1 " << pVtab[1][0] << endl;
cout << "\t" << "[1] B1dB1bptrB " << pVtab[1][1] << endl;
cout << "[2] B1::ib1 = " << (int)pVtab[2] << endl;
cout << "[3] B1::cb1 = " << (char)pVtab[3] << endl;
cout << "[4] = 0x" << (int*)pVtab[4]/*[0]*/ << endl;
cout << "[5] B::_vfptr->" << endl; //
cout << '\t' << "[" << 0 << "] thunk = 0x" << (int*)pVtab[5][0] << endl;
for (int i = 1; ; i++)
{
pFun = (Fun)pVtab[5][i];
cout << '\t' << "[" << i << "] 0x" << (int*)pFun << "\t";
if (pFun == NULL)
{
cout << endl;
break;
}
pFun();
}
cout << "[6] B::ib = " << (int)pVtab[6] << endl;
cout << "[7] B::ic = " << (char)pVtab[7] << endl;
}
代码输出了类B1的布局:
sizeof(B1) = 32
[0] B1::_vfptr->
[0] 0x0041106E B1::f1()
[1] 0x004110AF B1::Bf1()
[2] 0x00000000
[1] B1::_vbptr->
[0] B1dB1bptrB1 -4
[1] B1dB1bptrB 16
[2] B1::ib1 = 11
[3] B1::ic1 = 1
[4] = 0x00000000 //VC++中的一个NULL分隔符把B和B1的布局分开
[5] B::_vfptr->
[0] 0x00411055 //奇怪的地方:这个不是函数B1::f()的地址,因为执行这个地址的函数会出错,那么整个B1类的B1::f()虚函数在哪里呢?难道是“调整块”的地址?
[1] 0x004111F4 B::Bf()
[2] 0x00000000
[6] B::ib = 0
[7] B::ic = B
(2)钻石型虚拟继承
查看类D的内存布局的代码如下:
{
int** pVtab = NULL;
Fun pFun = NULL;
D d;
pVtab = (int**)&d;
cout << "sizeof(D) = " << sizeof(D) << endl;
cout << "[0] D::B1::_vfptr->" << endl;
for (int i = 0; ; i++)
{
pFun = (Fun)pVtab[0][i];
cout << '\t' << "[" << i << "] 0x" << (int*)pFun << "\t";
if (pFun == NULL)
{
cout << endl;
break;
}
pFun();
}
cout << "[1] D::B1::_vbptr->" << endl;
cout << "\t" << "[0] DdB1bptrB1 " << pVtab[1][0] << endl;
cout << "\t" << "[1] DdB1bptrB " << pVtab[1][1] << endl;
cout << "[2] B1::ib1 = " << (int)pVtab[2] << endl;
cout << "[3] B1::cb1 = " << (char)pVtab[3] << endl;
cout << "[4] D::B2::_vfptr->" << endl;
for (int i = 0; ; i++)
{
pFun = (Fun)pVtab[4][i];
cout << '\t' << "[" << i << "] 0x" << (int*)pFun << "\t";
if (pFun == NULL)
{
cout << endl;
break;
}
pFun();
}
cout << "[5] D::B2::_vbptr->" << endl;
cout << "\t" << "[0] DdB2bptrB2 " << pVtab[5][0] << endl;
cout << "\t" << "[1] DdB1bptrB " << pVtab[5][1] << endl;
cout << "[6] B2::ib2 = " << (int)pVtab[6] << endl;
cout << "[7] B2::cb2 = " << (char)pVtab[7] << endl;
cout << "[8] D::id = " << (int)pVtab[8] << endl;
cout << "[9] D::cd = " << (char)pVtab[9] << endl;
cout << "[10] = 0x" << (int*)pVtab[10]/*[0]*/ << endl;
cout << "[11] D::B::_vfptr->" << endl;
cout << '\t' << "[" << 0 << "] 0x" << (int*)pVtab[11][0] << endl;//奇怪的地方
for (int i = 1; ; i++)
{
pFun = (Fun)pVtab[11][i];
cout << '\t' << "[" << i << "] 0x" << (int*)pFun << "\t";
if (pFun == NULL)
{
cout << endl;
break;
}
pFun();
}
cout << "[12] B::ib = " << (int)pVtab[12] << endl;
cout << "[13] B::ic = " << (char)pVtab[13] << endl;
}
代码输出了类D的布局:
sizeof(D) = 56
[0] D::B1::_vfptr->
[0] 0x00411163 D::f1()
[1] 0x004110AF B1::Bf1()
[2] 0x004110D2 D::Df()
[3] 0x00000000
[1] D::B1::_vbptr->
[0] DdB1bptrB1 -4
[1] DdB1bptrB 40
[2] B1::ib1 = 11
[3] B1::cb1 = 1
[4] D::B2::_vfptr->
[0] 0x0041101E D::f2()
[1] 0x00411159 B2::Bf2()
[2] 0x00000000
[5] D::B2::_vbptr->
[0] DdB2bptrB2 -4
[1] DdB1bptrB 24
[6] B2::ib2 = 12
[7] B2::cb2 = 2
[8] D::id = 100
[9] D::cd = D
[10] = 0x00000000 //VC++中的一个NULL分隔符把B和B1和B2的布局分开
[11] D::B::_vfptr->
[0] 0x004110C3 //奇怪的地方:这个不是函数D::f()的地址,因为执行这个地址的函数会出错,那么整个D类的D::f()虚函数在哪里呢?难道是“调整块”的地址?
[1] 0x004111F4 B::Bf()
[2] 0x00000000
[12] B::ib = 0
[13] B::cb = B
目前我还不清楚是怎么回事,不知道是不是thunk之类的原因。