
我们可以看到在Base类的内存布局上,第一个位置上存放虚函数表指针,接下来才是Base的成员变量。另外,存在着虚函数表,该表里存放着Base类的所有virtual函数。
既然虚函数表指针通常放在对象实例的最前面的位置,那么我们应该可以通过代码来访问虚函数表,通过下面这段代码加深对虚函数表的理解:
#include "stdafx.h"
#include<iostream>
using namespace std;
class Base
{
public:
virtual void fun1(){
cout<<"base fun1!n";
}
virtual void fun2(){
cout<<"base fun2!n";
}
virtual void fun3(){
cout<<"base fun3!n";
}
int a;
};
int _tmain(int argc, _TCHAR* argv[])
{
typedef void(*pFunc)(void);
Base b;
cout<<"虚函数表指针地址:"<<(int*)(&b)<<endl;
//对象最前面是指向虚函数表的指针,虚函数表中存放的是虚函数的地址
pFunc pfun;
pfun=(pFunc)*((int*)(*(int*)(&b))); //这里存放的都是地址,所以才一层又一层的指针
pfun();
pfun=(pFunc)*((int*)(*(int*)(&b))+1);
pfun();
pfun=(pFunc)*((int*)(*(int*)(&b))+2);
pfun();
system("pause");
return 0;
}
运行结果:

通过这个例子,对虚函数表指针,虚函数表这些有了足够的理解。下面再深入一些。C++又是如何利用基类指针和虚函数来实现多态的呢?这里,我们就需要弄明白在继承环境下虚函数表是如何工作的。目前只理解单继承,至于虚继承,多重继承待以后再理解。
单继承代码如下:
class Base
{
public:
virtual void fun1(){
cout<<"base fun1!n";
}
virtual void fun2(){
cout<<"base fun2!n";
}
virtual void fun3(){
cout<<"base fun3!n";
}
int a;
};
class Child:public Base
{
public:
void fun1(){
cout<<"Child fun1n";
}
void fun2(){
cout<<"Child fun2n";
}
virtual void fun4(){
cout<<"Child fun4n";
}
};
内存布局对比:


通过对比,我们可以看到:
在单继承中,Child类覆盖了Base类中的同名虚函数,在虚函数表中体现为对应位置被Child类中的新函数替换,而没有被覆盖的函数则没有发生变化。 对于子类自己的虚函数,直接添加到虚函数表后面。另外,我们注意到,类Child和类Base中都只有一个vfptr指针,前面我们说过,该指针指向虚函数表,我们分别输出类Child和类Base的vfptr:
int _tmain(int argc, _TCHAR* argv[])
{
typedef void(*pFunc)(void);
Base b;
Child c;
cout<<"Base类的虚函数表指针地址:"<<(int*)(&b)<<endl;
cout<<"Child类的虚函数表指针地址:"<<(int*)(&c)<<endl;
system("pause");
return 0;
}










