测试环境:vs2013,windows 7 32 位系统
1.虚函数是放在虚函数表中的,因此我们可以通过访问虚函数表来找到相应的虚函数入口地址,从而调用虚函数。
2.由于虚函数是放在虚函数表中的,所以在继承的时候,其会放到子类的虚函数表中(如果子类重写了父类中虚函数则父类中相应虚函数在子类的虚函数表中就会被覆盖)。
3.虚函数表中存放虚函数顺序是先父类的(如果子类重写了父类的则直接替换为子类的)再放子类的虚函数。值得强调的是在子类对象中通过虚函数表中也是可以访问父类私有的虚函数(前提是子类没有重写父类中相应的虚函数)。
下面一段程序就是通过虚函数表和函数指针来访问虚函数。
#include <iostream>
using namespace std;
class A
{
private:
virtual void print()
{
cout << "A::print()" << endl;
}
virtual void go()
{
cout << "A::go()" << endl;
}
};
class B :public A
{
virtual void go()
{
cout << "B::go()" << endl;
}
virtual void f()
{
cout << "B::f()" << endl;
}
};
typedef void (*pFun) (void);//
void main()
{
B b;
pFun fun;
cout << "A size:" << sizeof(class A) << endl;//结果是4 有一个指指向虚函数表的指针
cout << "B size:" << sizeof(class B) << endl; // 结果是4 有一个指指向虚函数表的指针
for (int i = 0; i < 3; i++)//一共有3个虚函数(子类覆盖了一个 go)
{
fun = (pFun)*((int *)*(int *)(&b) + i);
fun();
}
cin.get(); //让程序等待下
}
从运行的结果来看,通过虚函数表也可以访问父类的私有虚函数virtual void print(),virtual void go()已经被子类重写。
fun = (pFun)*((int *)*(int *)(&b) + i);// 这条语句的理解
如下图所示,首先取得0x00002500这个虚函数表的首地址值,不是取b的内存对象地址值0x00001234。首先获得对象b的内存地址0x00001234,将其转换为int * 指针类型,就是读取内存4个字节,(int *)(&b),然后再解引取出内存地址4个字节对应的值0x00002500(虚函数表的首地址值)再使用int * 将其转换int * 指针(从首地址开始读取四个字节),(int *)*(int *)(&b),然后再解引获得这四个字节的值转换为函数指针。加i是指针步长,每次移动四个字节。