—
虚函数表
第一个函数地址: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