background image

            Fun pFun = NULL;
 
            cout << "虚函数表地址:" << (int*)(&b) << endl;
            cout << "

 — 

虚函数表

第一个函数地址:" << (int*)*(int*)(&b) << endl;

 
            // Invoke the first virtual function 
            pFun = (Fun)*((int*)*(int*)(&b));
            pFun();
实际运行经果如下:(Windows XP+VS2003,  Linux 2.6.22 + GCC 4.1.3)
虚函数表地址:0012FED4

 — 

虚函数表

第一个函数地址:0044F148

Base::f
 
 
通过这个示例,我们可以看到,我们可以通过强行把&b 转成 int *,取得虚函数表的地址,
然后,再次取址就可以得到第一个虚函数的地址了,也就是 Base::f(),这在上面的程序中
得到了验证(把 int* 强制转成了函数指针)。通过这个示例,我们就可以知道如果要调用
Base::g()和 Base::h(),其代码如下:
            (Fun)*((int*)*(int*)(&b)+0);  // Base::f()
            (Fun)*((int*)*(int*)(&b)+1);  // Base::g()
            (Fun)*((int*)*(int*)(&b)+2);  // Base::h()
 
这个时候你应该懂了吧。什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让我
画个图解释一下。如下所示:

注意:在上面这个图中,我在虚函数表的最后多加了一个结点,这是虚函数表的结束结

点,就像字符串的结束符 \0”一样,其标志了虚函数表的结束。这个结束标志的值在不同
的编译器下是不同的。在 WinXP+VS2003 下,这个值是 NULL。而在 Ubuntu 7.10 + Linux 
2.6.22 + GCC 4.1.3 下,这个值是如果 1,表示还有下一个虚函数表,如果值是 0,表示是
最后一个虚函数表。

” “

下面,我将分别说明 无覆盖 和 有覆盖 时的虚函数表的样子。没有覆盖父类的虚函数是
毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,
我们可以更加清楚地知道其内部的具体实现。

一般继承(无虚函数覆盖)

下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系: