class base
{
int i;
public:
base(int I):i(I){}
virtual int sum(){return i;}
};
主程序
base b(100);
b.sum();
看base的构造函数
class base
{
int i;
public:
base(int I):i(I){}
00401250 push ebp
00401251 mov ebp,esp
00401253 push ecx
00401254 mov dword ptr [ebp-4],ecx
00401257 mov eax,dword ptr [this]
0040125A mov dword ptr [eax],offset base::`vftable' (402164h) //对象首地址写入offset vftable
00401260 mov ecx,dword ptr [this]
00401263 mov edx,dword ptr [I] //i 写入edx
00401266 mov dword ptr [ecx+4],edx //edx写入首地址+4
00401269 mov eax,dword ptr [this] //返回this
0040126C mov esp,ebp
0040126E pop ebp
0040126F ret 4
我们看到内存 402164放着401280
virtual int sum(){return i;}
00401280 push ebp
00401281 mov ebp,esp
00401283 push ecx
00401284 mov dword ptr [ebp-4],ecx
00401287 mov eax,dword ptr [this]
0040128A mov eax,dword ptr [eax+4]
0040128D mov esp,ebp
0040128F pop ebp
00401290 ret
现在有了个小结论:
vptr的初始化在构造函数内,由编译器悄悄生成, 成员初始化之前,问题来了 如果在构造函数内调用未被初始化的成员,会发生什么?
什么都没发生,一切正常
class base
{
int i;
public:
base(int I):i(I){add();}
00401250 push ebp
00401251 mov ebp,esp
00401253 push ecx
00401254 mov dword ptr [ebp-4],ecx
00401257 mov eax,dword ptr [this]
0040125A mov dword ptr [eax],offset base::`vftable' (402164h)
00401260 mov ecx,dword ptr [this]
00401263 mov edx,dword ptr [I]
00401266 mov dword ptr [ecx+4],edx
00401269 mov ecx,dword ptr [this]
0040126C call base::add (4012A0h) //不论是不是虚函数,都在成员初始化之后
00401271 mov eax,dword ptr [this]
00401274 mov esp,ebp
00401276 pop ebp
00401277 ret 4
不清楚各个编译器的是不是不同,这是我在vc2005下的结果
再看派生类的,从汇编可以看出,是用派生类的vptr 代替了base的vptr
base(int I):i(I){printf("base vptr=%x",*(int*)this);}
derived(int I,int J):base(I),j(J) {printf("derived vptr=%x",*(int*)this);}
在构造函数里直接输出了vptr
base(int I):i(I){printf("base vptr=%x",*(int*)this);}
00401260 push ebp
00401261 mov ebp,esp
00401263 push ecx
00401264 mov dword ptr [ebp-4],ecx
00401267 mov eax,dword ptr [this]
0040126A mov dword ptr [eax],offset base::`vftable' (402174h)
00401270 mov ecx,dword ptr [this]
00401273 mov edx,dword ptr [I]
00401276 mov dword ptr [ecx+4],edx
00401279 mov eax,dword ptr [this]
0040127C mov ecx,dword ptr [eax]
0040127E push ecx
0040127F push offset string "base vptr=%x" (402160h)
00401284 call dword ptr [__imp__printf (4020ACh)]
0040128A add esp,8
0040128D mov eax,dword ptr [this]
00401290 mov esp,ebp
00401292 pop ebp
00401293 ret 4
class derived:public base
{
int j;
public:
derived(int I,int J):base(I),j(J) {printf("derived vptr=%x",*(int*)this);}
004012C0 push ebp
004012C1 mov ebp,esp
004012C3 push ecx
004012C4 mov dword ptr [ebp-4],ecx
004012C7 mov eax,dword ptr [I]
004012CA push eax
004012CB mov ecx,dword ptr [this]
004012CE call base::base (401260h)
004012D3 mov ecx,dword ptr [this]
004012D6 mov dword ptr [ecx],offset derived::`vftable' (40218Ch)
004012DC mov edx,dword ptr [this]
004012DF mov eax,dword ptr [J]
004012E2 mov dword ptr [edx+8],eax
004012E5 mov ecx,dword ptr [this]
004012E8 mov edx,dword ptr [ecx]
004012EA push edx
004012EB push offset string "derived vptr=%x" (402178h)
004012F0 call dword ptr [__imp__printf (4020ACh)]
004012F6 add esp,8
004012F9 mov eax,dword ptr [this]
004012FC mov esp,ebp
004012FE pop ebp
004012FF ret 8
主程序
base b(100);
derived d(200,400);
结果输出了3个
base vptr=402174 base vptr=402174 derived vptr=40218c
是不是跟你想的一样?