You are on page 1of 6

这两天试了一下,来说两句

因手头上只有 vc6 编译器,故只看了 vc6 的方式

我的测试程序如下
#include "stdafx.h"

class mostbase1
{
public:
mostbase1():i(1){};
int i;
};

class mostbase2
{
public:
mostbase2():j(2){};
int j;
};

class base1:public virtual mostbase1,public virtual mostbase2


{
};
class base2:public virtual mostbase1,public virtual mostbase2
{
};
class derived:public base1,public base2
{
};
void f(derived* pderived)
{
mostbase1* pbase1 = pderived;
mostbase2* pbase2 = pderived;
int k = pderived->i;
k = pderived->j;
k = pbase1->i;
k = pbase2->j;
}
int main(int argc, char* argv[])
{
derived d;
f(&d);
printf("Hello World! ");
return 0;
}
经过我的测试及查看汇编代码,得知 vc 的虚基类转换确如 Inside the c++ objects model 中所说是用
一个 virtual base class table 来实现的
base1 和 base2 中各有一个__vbct__ptr(大小是 4 个字节)用来指向一个 virtual base class table,
该表格存储着 base 类中虚基类在 base 类中的偏移,其后就是数据,当 derived 继承自 base1 和
base2,首先是存放 base1 和 base2 的__vbct_ptr,再就是数据,将 base1 和 base2 相同基类的数据
放到一起
base1 类的内存布局如下(base2 与 base1 相同):
pvoid* __vbct_base1 ----先是虚基类表的地址,此时__vbct_base1 数据如下
----00 00 00 00 04 00 00 00 08 00 00 00
----mostbase1 在 base1 中的偏移为[__vbct_base1+4]的值
int i; ----mostbase1 类中的 i
int j; ----mostbase2 类中的 j

derived 类的内存布局如下:
pvoid* __vbct_base1; ----base1 类的虚基类表的地址,其中的值有变化,数据如下
----00 00 00 00 08 00 00 00 0C 00 00 00
pvoid* __vbct_base2; ----base2 类的虚基类表的地址,数据如下
----00 00 00 00 04 00 00 00 08 00 00 00
int i;
int j;

下面是 f(derived* pderived)函数的汇编代码,对其它进行分析

29: void f(derived* pderived)


30: {
0040C230 push ebp
0040C231 mov ebp,esp
0040C233 sub esp,60h
0040C236 push ebx
0040C237 push esi
0040C238 push edi
0040C239 lea edi,[ebp-60h]
0040C23C mov ecx,18h
0040C241 mov eax,0CCCCCCCCh
0040C246 rep stos dword ptr [edi]
31: mostbase1* pbase1 = pderived;
0040C248 cmp dword ptr [ebp+8],0 [1]
0040C24C jne f+27h (0040c257)
0040C24E mov dword ptr [ebp-18h],0 [2]
0040C255 jmp f+35h (0040c265)
0040C257 mov eax,dword ptr [ebp+8]
0040C25A mov ecx,dword ptr [eax] [3]
0040C25C mov edx,dword ptr [ebp+8]
0040C25F add edx,dword ptr [ecx+4] [4]
0040C262 mov dword ptr [ebp-18h],edx
0040C265 mov eax,dword ptr [ebp-18h]
0040C268 mov dword ptr [ebp-4],eax [5]
32: mostbase2* pbase2 = pderived;
0040C26B cmp dword ptr [ebp+8],0
0040C26F jne f+4Ah (0040c27a)
0040C271 mov dword ptr [ebp-1Ch],0 [6]
0040C278 jmp f+58h (0040c288)
0040C27A mov ecx,dword ptr [ebp+8]
0040C27D mov edx,dword ptr [ecx] [7]
0040C27F mov eax,dword ptr [ebp+8]
0040C282 add eax,dword ptr [edx+8] [8]
0040C285 mov dword ptr [ebp-1Ch],eax
0040C288 mov ecx,dword ptr [ebp-1Ch]
0040C28B mov dword ptr [ebp-8],ecx
33: base1* p1 = pderived;
0040C28E mov edx,dword ptr [ebp+8]
0040C291 mov dword ptr [ebp-0Ch],edx [9]
34: p1->i = 1;
0040C294 mov eax,dword ptr [ebp-0Ch]
0040C297 mov ecx,dword ptr [eax]
0040C299 mov edx,dword ptr [ecx+4] [10]
0040C29C mov eax,dword ptr [ebp-0Ch]
0040C29F mov dword ptr [eax+edx],1
35: base2* p2 = pderived;
0040C2A6 cmp dword ptr [ebp+8],0
0040C2AA je f+87h (0040c2b7)
0040C2AC mov ecx,dword ptr [ebp+8]
0040C2AF add ecx,4 [11]
0040C2B2 mov dword ptr [ebp-20h],ecx
0040C2B5 jmp f+8Eh (0040c2be)
0040C2B7 mov dword ptr [ebp-20h],0
0040C2BE mov edx,dword ptr [ebp-20h]
0040C2C1 mov dword ptr [ebp-10h],edx [12]
36: p2->j = 1;
0040C2C4 mov eax,dword ptr [ebp-10h]
0040C2C7 mov ecx,dword ptr [eax]
0040C2C9 mov edx,dword ptr [ecx+8] [13]
0040C2CC mov eax,dword ptr [ebp-10h]
0040C2CF mov dword ptr [eax+edx],1
37: int k = pderived->i;
0040C2D6 mov ecx,dword ptr [ebp+8]
0040C2D9 mov edx,dword ptr [ecx]
0040C2DB mov eax,dword ptr [edx+4] [14]
0040C2DE mov ecx,dword ptr [ebp+8]
0040C2E1 mov edx,dword ptr [ecx+eax] [15]
0040C2E4 mov dword ptr [ebp-14h],edx
38: k = pderived->j;
0040C2E7 mov eax,dword ptr [ebp+8]
0040C2EA mov ecx,dword ptr [eax]
0040C2EC mov edx,dword ptr [ecx+8] [16]
0040C2EF mov eax,dword ptr [ebp+8]
0040C2F2 mov ecx,dword ptr [eax+edx]
0040C2F5 mov dword ptr [ebp-14h],ecx
39:
40: }
0040C2F8 pop edi
0040C2F9 pop esi
0040C2FA pop ebx
0040C2FB mov esp,ebp
0040C2FD pop ebp
0040C2FE ret

[1]dword ptr [ebp+8h]就是 pderived,先看看是不是为 NULL.


[2]dword ptr [ebp-18h]是一个中间变量,当 pderived 为 NULL 时,将其也赋为 NULL
[3]取出__vbct_base1,其位置在 derived 类的开始处
[4]取出 mostbast1 类在 derived 中的偏移,此值在_vbct_base1+4 的位置,占用 4 个字节,其值为
8, 因为 derived 前有两个__vbct_prt,都为 4 字节,故 mostbast1 在 derived 的偏移为 8.
[5]将 pbase1 赋值,dword ptr [ebp-4]存放 pbase1;
[6]同(2),只是中间变量的地址不同
[7]同(3),取出__vbct_base1
[8]同(4), 取出 mostbast2 类在 derived 中的偏移,此时为 12
[9]取出 derived 类中的 base1 的地址,也就是 derived 中__vbct_base1 的地址,可能你有疑问,看下

[10]用 p1 存取数据时,还是通过__vbct_base1 来做的,通过__vbct_base1 得到 mostbase1 在
derived 中 的偏移,最后得到的地址是 pderived+8
[11]现在取出__vbct_base2 的地址,看到了 add ecx,4 么
[12]将 p2 赋值
[13]通过__vbct_base2 来取得 mostbase2 的地址,再来存取 j
[14]通过__vbct_base1 来取得 mostbase1 的地址
[15]偏移在 eax 中,取出 i 来
[16]通过__vbct_base1 来取得 mostbase2 的地址

以下是我构想的 c 伪码,可能不太正确,因为汇编代码已经优化过
void f(derived* pderived)
{
----mostbase1* pbase1 = pderived;
mostbase1 *pbase1,*temp1;
if (pderived == 0)
{
temp1 = 0;
}
else
{
temp1 = (mostbase1*)(pderived+(pderived->__vbct_base1[1]));
}
pbase1 = temp1;

----mostbase2* pbase2 = pderived;


mostbase2 *pbase2,*temp2;
if (pderived == 0)
{
temp2 = 0;
}
else
{
temp2 = (mostbase2*)(pderived+(pderived->__vbct_base1[2]));
}
pbase2 = temp2;
----base1* p1 = pderived;
base1* p1 = &pderived->__vbct_base1;
----p1->i = 1;
(mostbase1*)(p1+p1->__vbct_base1[1])->i = 1;
----base2* p2 = pderived;
base2* p2 = &pderived->__vbct_base2;
----p2->j = 1;
(mostbase2*)(p2+p2->__vbct_base2[2])->j = 1;
----int k = pderived->i;
int k = (mostbase1*)(pderived+pderived->__vbct_base1[1])->i;
----k = pderived->j;
k = (mostbase2*)(pderived+pderived->__vbct_base1[2])->j;
}

You might also like