在本文中,我们来学习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;
- }
这个程序稍微有点复杂,下面,我们来画图分析:
我们调试上述程序,得到下图:
上述三个图,交相辉映,现在,有点明白虚函数表了吧。