(感谢http://blog.csdn.net/haoel/article/details/1948051/)
C++中的虚函数的作用主要是实现了多态的机制。
多态,简而言之就是用父类型别的指针指向其子类的实例,然后通过父类的指针调用实际子类的成员函数。这种技术可以让父类的指针有“多种形态”,这是一种泛型技术。
虚函数(Virtual Function)是通过一张虚函数表(Virtual Table)来实现的,简称为V-Table。在这个表中记录了一个类的虚函数的地址表,这张表解决了继承、覆盖的问题。C++的编译器应该是保证虚函数表的指针存在于对象实例中最前面的位置,所以当我们用父类的指针来操作一个子类的时候,这张虚函数表就像一个地图一样,指明了实际所应该调用的函数。
假设有这样一个基类:
1 class Base {
2 public:
3 virtual void f() { cout << "Base::f" << endl; }
4 virtual void g() { cout << "Base::g" << endl; }
5 virtual void h() { cout << "Base::h" << endl; }
6 };
在虚函数表的最后有一个结点,这是虚函数表的结束结点,就像字符串的结束符“/0”一样,标志了虚函数表的结束。
1 typedef void(*Fun)(void);
2 Base b;
3 Fun pFun = NULL;
4
5 cout << "虚函数表地址:" << (int*)(&b) << endl;
6 cout << "虚函数表 — 第一个函数地址:" << (int*)*(int*)(&b) << endl;
7
8 // Invoke the first virtual function
9 pFun = (Fun)*((int*)*(int*)(&b));
10 pFun();
11
12 (Fun)*((int*)*(int*)(&b)+0); // Base::f()
13 (Fun)*((int*)*(int*)(&b)+1); // Base::g()
14 (Fun)*((int*)*(int*)(&b)+2); // Base::h()
可以通过强行把&b转成int *,取得虚函数表的地址,然后,再次取址就可以得到第一个虚函数的地址了,也就是Base::f()。
一般继承(无虚函数覆盖)
子类没有重载任何父类的函数。对于实例Derive d的虚函数表如下:
1)虚函数按照其声明顺序放于表中。
2)父类的虚函数在子类的虚函数前面。
一般继承(有虚函数覆盖)
如果子类中有虚函数重载了父类的虚函数。
1)覆盖的f()函数被放到了虚表中原来父类虚函数的位置。
2)没有被覆盖的函数依旧。