关于虚函数调用之我的一点理解
先来看普通函数调用。
有类如下:
class A
{
public:
void callfunc(int a,int ,int )
{
printf("A::callfunc\r\n");
}
};
int main()
{
A *pA=NULL;
pA->callfunc();
return 0;
}
惊奇地发现,调用函数无需任何空间,无需实例化类,只需定义一个类A的指针即可,这说明了,类内函数的调用,不依靠对像传递任何东西。只要编译器识别到指针的类型(属于哪个类)即可。
当然,有一种情况例外,那就是。。。虚函数
看以下代码:
#include
#include
class A
{
public:
void f1()
{
printf("A::f1\r\n");
}
virtual void f2()
{
printf("A::f2\r\n");
}
void callfunc()
{
printf("A::callfunc\r\n");
f1();
f2();
}
};
class B :public A
{
public:
int aabb;
void f1()
{
printf("B::f1\r\n");
}
void f2()
{
printf("B::f2\r\n");
}
void callfunc()
{
printf("B::callfunc\r\n");
f1();
f2();
}
void fb()
{
;
}
};
int main()
{
B *pB=new B;
A *pA=NULL;
pA=pB;
pA->callfunc();
return 0;
}
若此时去掉pA=pB将会在类A中的f1(),f2()报错,答案是。。。虚函数和普通函数是不一样的,执行虚函数需要v-table,程序执行到f1(),f2()发现虚函数,但没有v-table无法从找到函数入口地址,故直接报错,那为毛pA=pB就可以正确执行了呢?又为毛输出的是A的callfuncB的f1,f2呢?
一般地,对像起始地址存的是vptr,这个指针指向的是v-talbe,可以通过v-table(函数指针数组,只存虚函数,有虚函数的类必定有v-talbe)去索引虚函数入口地址列表,当然,是B类的v-table。故函数执到类A的f1() f2()时,先从pA的起始地址(即pB的起始地址)获得了vptr,再据vptr找到对对应类的v-talbe,从v-talbe中找到了B类的函数,故这之后直接跳到B类去了。