有时候脑袋短路,加上记忆力退化,很多已知的概念又模糊混淆了。唉,杯具。
今天就在想一个拥有虚函数的基类,它的对象是不是没有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!