在本文中,我们来学习C++中的虚函数表。很喜欢苏轼的《题西林壁》:
横看成岭侧成峰,远近高低各不同。
不识庐山真面目,只缘身在此山中。
这首诗的哲理在于:对于同一个东西,从不同的角度看,结果是不一样的。下面来看一些铺垫性的程序:
#include <iostream>
using namespace std;
int main()
{
float f = 3.14;
cout << f << endl; // 3.14
cout << &f << endl; // 0012FF7C
cout << (int *)(&f) << endl; // 0012FF7C
cout << *((int *)(&f)) << endl; // 1078523331
return 0;
}
#include <iostream>
using namespace std;
int main()
{
float **pp;
float *p;
float a = 10.5;
p = &a;
pp = &p;
cout << pp << endl; // 0012FF78
cout << p << endl; // 0012FF74
cout << a << endl; // 10.5
cout << "---------" << endl;
#define ADDR 0x0012ff78
cout << (float **)ADDR << endl; // 0012FF78
cout << *(float **)ADDR << endl; // 0012FF74
cout << *(*(float **)ADDR) << endl; // 10.5
cout << "---------" << endl;
cout << (int **)ADDR << endl; // 0012FF78
cout << *(int **)ADDR << endl; // 0012FF74
cout << *(*(int **)ADDR) << endl; // 1093140480
cout << *((float *)(*(int **)ADDR)) << endl; // 10.5
cout << "---------" << endl;
return 0;
}
#include <iostream>
using namespace std;
int main()
{
cout << (short *)0 + 1 << endl; // 00000002
cout << (int *)0 + 1 << endl; // 00000004
cout << (long *)0 + 1 << endl; // 00000004
cout << (float *)0 + 1 << endl; // 00000004
cout << (double *)0 + 1 << endl; // 00000008
return 0;
}
搞懂了上面三个程序,就弄懂了苏轼的那首诗。同样的内存单元,不同的解析方式,可以得到不同的结果,下面来进入虚函数表的话题。看程序:
#include <iostream>
using namespace std;
class A
{
public:
virtual void fun();
};
int main()
{
cout << sizeof(A) << endl; // 4
return 0;
}
古怪,结果怎么会是4呢?这是虚指针在作怪!哪来的虚指针哦?看下面的程序:
#include <iostream>
using namespace std;
class E
{
public:
// 为了方便叙述,故把公开a和b, 在实际系统中,很少这样做
int a;
int b;
virtual void f()
{
};
virtual void g()
{
};
};
int main()
{
E e;
e.a = 1;
e.b = 2;
cout << &e << endl; // e的VTABLE的地址: 0012FF74
cout << (int *)&e << endl; // eVPTR的地址: 0012FF74
cout << &e.a << endl; // e.a的地址: 0012FF78
cout << &e.b << endl; // e.b的地址: 0012FF7C
cout << (void *)*((int *)&e) << endl; // eVPTR的值 0046F028
cout << *((int *)&e + 1) << endl; // e.a的值: 1
cout << *((int *)&e + 2) << endl; // e.b的值: 2
cout << (void *)*(int *)(*(int *)&e) << endl; // E的f函数的地址: 0040128A
cout << (void *)*((int *)(*(int *)&e) + 1) << endl; // E的g函数的地址: 004011B8
cout << "---------" << endl;
E ee;
ee.a = 3;
ee.b = 4;
cout << &ee << endl; // ee的VTABLE的地址: 0012FF68
cout << (int *)&ee << endl; // eeVPTR的地址: 0012FF68
cout << &ee.a << endl; // ee.a的地址: 0012FF6C
cout << &ee.b << endl; // ee.b的地址: 0012FF70
cout << (void *)*((int *)&ee) << endl; // eVPTR的值 0046F028
cout << *((int *)&ee + 1) << endl; // ee.a的值: 3
cout << *((int *)&ee + 2) << endl; // ee.b的值: 4
cout << (void *)*(int *)(*(int *)&ee) << endl; // E的f函数的地址: 0040128A
cout << (void *)*((int *)(*(int *)&ee) + 1) << endl; // E的g函数的地址: 004011B8
return 0;
}
这个程序稍微有点复杂,下面,我们来画图分析:
我们调试上述程序,得到下图:
上述三个图,交相辉映,现在,有点明白虚函数表了吧。