C++虚函数表解析 [二]
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的。在这个表中,主要是一个类的虚函数的地址,这张表解决了继承、覆盖的问题,其内容真实反映了实际函数。这样,在有虚函数的类的实例中虚函数表被分配在实例的内存中,所以,当用父类的指针从操作一个子类的时候,这张虚函数表就显得尤为重要了,它就像一张地图,指明了实际所应该调用的函数。
在C++的标准规格说明书中写道,编译器必须保证虚函数表的指针存在于对象实例中最前面的位置(这是为了保证正确取到虚函数的偏移量)。这意味着通过对象实例的地址可以得到虚函数表,然后可以遍历其中的函数指针,并调用相应的函数了。请看下面的代码
class Base{
public:
virtual void fun1(){cout<<"Base::fun1"<<endl;}
virtual void fun2(){cout<<"Base::fun2"<<endl;}
virtual void fun3(){cout<<"Base::fun3"<<endl;}
private:
int num1;
int num2;
};
typedef void(*Fun)(void);
int main(int argc, char* argv[])
{
Base b;
Fun pFun;
pFun=(Fun)*((int*)*(int*)(&b)+0);
pFun();
pFun=(Fun)*((int*)*(int*)(&b)+1);
pFun();
pFun=(Fun)*((int*)*(int*)(&b)+2);
pFun();
return 0;
}
上面程序的执行结果如下:
Base::fun1
Base::fun2
Base::fun3
可以看到通过函数指针pFun的调用,分别执行了对象b的3个虚函数。通过这个示例发现,可以强行把&b转成int*,取得虚函数表的地址,然后再次取址就可以得到第一个虚函数的地址了,也就是Base::fun1()。如果调用Base::fun2()和Base::fun3(),只需要把&b先加上数组元素的偏移,后面的步骤类似。
程序中Base对象b的内存结构图,如下图所示:
摘自:日新为道的专栏
补充:软件开发 , C++ ,