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,表示是
最后一个虚函数表。
“
” “
”
下面,我将分别说明 无覆盖 和 有覆盖 时的虚函数表的样子。没有覆盖父类的虚函数是
毫无意义的。我之所以要讲述没有覆盖的情况,主要目的是为了给一个对比。在比较之下,
我们可以更加清楚地知道其内部的具体实现。
一般继承(无虚函数覆盖)
下面,再让我们来看看继承时的虚函数表是什么样的。假设有如下所示的一个继承关系: