background image

 — 

虚函数表

第一个函数地址: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()

这个时候你应该懂了吧!什么?还是有点晕。也是,这样的代码看着太乱了。没问题,让
我画个图解释一下。如下所示:

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

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

 

,表示是最后一个虚函数表。

” “

下面,我将分别说明 无重载 和 有重载 时的虚函数表的样子。没有重载父类的虚函数是
毫无意义的。我之所以要讲述没有重载的情况,主要目的是为了给一个对比。在比较之下,

 

我们可以更加清楚地知道其内部的具体实现。

 

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

图 2