有时候脑袋短路,加上记忆力退化,很多已知的概念又模糊混淆了。唉,杯具。
今天就在想一个拥有虚函数的基类,它的对象是不是没有vptr和vtable?因为它本身很确定了,如下:
class CA
{
public:
 virtual void Func1()
 {
  printf("CA::Func1\n");
 }
};
CA a;
printf("sizeof(a)=%d, sizeof(CA)=%d\n", sizeof(a), sizeof(CA));
a.Func1();
事实是sizeof均为4,也就是说有vptr和vtable!而a.Func1的调用也确实是很确定的:
00424591  lea         ecx,[a]
00424594  call        CA::Func1 (41564Ah)
但若使用指针调用则:
CA *pa = &a;
0042D241  lea         eax,[a]
0042D244  mov         dword ptr [pa],eax
pa->Func1();
0042D247  mov         eax,dword ptr [pa]
0042D24A  mov         edx,dword ptr [eax]
0042D24C  mov         esi,esp
0042D24E  mov         ecx,dword ptr [pa]
0042D251  mov         eax,dword ptr [edx]
0042D253  call        eax
很明显是通过vptr和vtable实现的。想想多态的概念也确实是虚函数+指针/引用的结合实现的。
因此一个类只要拥有虚函数则一定是拥有vptr和vtable的,编译器可以不用区分这是不是多态(指针/引用的对象是基类CA的还是子类CB的)来分别生成代码,如下:
CA *pa = &b;
0042D264  lea         eax,[b]
0042D267  mov         dword ptr [pa],eax
pa->Func1();
0042D26A  mov         eax,dword ptr [pa]
0042D26D  mov         edx,dword ptr [eax]
0042D26F  mov         esi,esp
0042D271  mov         ecx,dword ptr [pa]
0042D274  mov         eax,dword ptr [edx]
0042D276  call        eax 
又在想另一个问题:若一个拥有虚函数的子类继承自一个拥有虚函数的基类,它的对象是不是有两套vptr和vtable?如下
class CB : public CA
{
public:
 virtual void Func2()
 {
  printf("CB::Func2\n");
 }
};
答案是不需要,因为可以通过一套vptr和vtable调用到各自的成员函数,只有多重继承时才会有多套vptr和vtable!