在c++中多态性可大致分为两类:静态多态和动态多态。
函数重载和运算符重载实现的多态属于静态多态,动态多态性是通过虚函数实现的。
每个含有虚函数的类有一张虚函数表(vtbl),该表是一个一维的数组,表中每一项是一个虚函数的地址, 也就是说,虚函数表的每一项是一个虚函数的指针。没有虚函数的C++类,是不会有虚函数表的。
两张图:
下面是一些简单例子,是在vc6.0中编译的:
#include <iostream> #include <windows.h> using namespace std; class base { virtual void f(){cout<<"base::f"<<endl;}; virtual void g(){cout<<"base::g"<<endl;}; virtual void h(){cout<<"base::h"<<endl;}; }; typedef void (*pfun)(); void main() { DWORD w=0x4011e0; //虚函数表第一项的内容,也就是第一个虚函数的地址 pfun fun=NULL; base b; base *pbase=&b; fun=(pfun)w; fun(); //调用第一个虚函数 }编译查看对象b在内存中的情况:
查看虚函数表:
虚函数表的指针4个字节大小(vptr),存在于对象实例中最前面的位置(这是为了保证取到虚函数表的有最高的性能——如果有多层继承或是多重继承的情况下)。这意味着我们通过对象实例的地址得到这张虚函数表,然后就可以遍历其中函数指针,并调用相应的函数。
注意:虚函数表的结束标志在不同的编译器下是不同的。在VC6.0下,这个值是NULL
另外再补充一个例子
#include <iostream> using namespace std; class base { virtual void f(){cout<<"base::f"<<endl;}; virtual void g(){cout<<"base::g"<<endl;}; virtual void h(){cout<<"base::h"<<endl;}; }; class Derive : public base { public: Derive(){}; virtual void f() { cout << "Derive::f" << endl; } virtual void g() { cout << "Derive::g" << endl; } }; typedef void(*pfun)(); void main() { pfun fun=NULL; Derive d; base *p=&d; fun=(pfun)**((int**)p); fun(); //调用第一个虚函数 fun=(pfun)*(*((int**)p)+2); fun(); //调用第三个函数 }
在多重继承中的情况:
有几个父类,就有几个vtab和vptr
具体的代码如下:
#include <iostream> using namespace std; class Base1 { public: virtual void f() { cout << "Base1::f" << endl; } virtual void g() { cout << "Base1::g" << endl; } virtual void h() { cout << "Base1::h" << endl; } }; class Base2 { public: virtual void f() { cout << "Base2::f" << endl; } virtual void g() { cout << "Base2::g" << endl; } virtual void h() { cout << "Base2::h" << endl; } }; class Base3 { public: virtual void f() { cout << "Base3::f" << endl; } virtual void g() { cout << "Base3::g" << endl; } virtual void h() { cout << "Base3::h" << endl; } }; class Derive : public Base1, public Base2, public Base3 { public: virtual void f() { cout << "Derive::f" << endl; } virtual void g1() { cout << "Derive::g1" << endl; } }; typedef void(*Fun)(void); int main() { Fun pFun = NULL; Derive d; int** pVtab = (int**)&d; //Base1's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+0); pFun = (Fun)pVtab[0][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+1); pFun = (Fun)pVtab[0][1]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+2); pFun = (Fun)pVtab[0][2]; pFun(); //Derive's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+0)+3); pFun = (Fun)pVtab[0][3]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[0][4]; cout<<pFun<<endl; //Base2's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[1][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[1][1]; pFun(); pFun = (Fun)pVtab[1][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[1][3]; cout<<pFun<<endl; //Base3's vtable //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+0); pFun = (Fun)pVtab[2][0]; pFun(); //pFun = (Fun)*((int*)*(int*)((int*)&d+1)+1); pFun = (Fun)pVtab[2][1]; pFun(); pFun = (Fun)pVtab[2][2]; pFun(); //The tail of the vtable pFun = (Fun)pVtab[2][3]; cout<<pFun<<endl; cout<<sizeof(d)<<endl; return 0; }