以下面代码为例:
class A
{
public:
int a;
A()
{
a = 1;
}
virtual void fA()
{
printf("A\n");
}
void fAA()
{
printf("AA\n");
}
};
class B
{
public:
int b;
B()
{
b = 2;
}
virtual void fB(){}
};
class C : public A
{
public:
int c;
C()
{
c = 3;
}
void fA()
{
printf("C\n");
}
void fAA()
{
printf("AAa\n");
}
};
void main()
{
C c;
A* a = &c;
printf("%d\n", sizeof(c));
printf("%d\n", sizeof(*a));
a->fA();
a->fAA();
getchar();
}
首先C c; 创建一个C类型的对象c,类C是继承A的。在vs2012中调试,在监视中查看c的内容,如下图所示:
可以看到c对象继承了A对象的_vfptr、变量a,然后还有自身的变量c。_vfptr是指向vtable虚函数表的指针,可以看到该表中存放了一个函数指针C::fA(),因为在A中声明了虚函数fA(),并且在C中进行了重写,所以虚函数表对应位置填写的是C实现的fA()的函数地址。所以c的大小是12B。
A* a = &c; *a的大小为8B。
接着调用a->fA(); 看下在vs2012中反汇编的结果:
a->fA();
011D361E mov eax,dword ptr [a] // 获取a指向对象的地址
011D3621 mov edx,dword ptr [eax] // 获取_vfptr指向的地址
011D3623 mov esi,esp
011D3625 mov ecx,dword ptr [a]
011D3628 mov eax,dword ptr [edx] // 获取vtable中第一个虚函数指向的地址
011D362A call eax // 调用C::fA()
011D362C cmp esi,esp
011D362E call __RTC_CheckEsp (011D12F3h)
再调用a->fAA(); 该函数并不是虚函数,地址不存放在虚函数表中,可以直接根据类名查找到函数地址进行调用,反汇编结果如下:
a->fAA();
011D3633 mov ecx,dword ptr [a]
011D3636 call A::fAA (011D1456h)