通常我们会认为 c++ 程序中,类的虚函数总是严格按照类中的声明顺序排列,但最近我在查看一个虚函数的调用时却发现并不如此。如果虚函数有候选的重载,那它的顺序实际可能和声明的顺序不一样。
比如下面一个简单的类
class VTableOrder { public: virtual int F1(int) { return 1; } virtual int F2(int) { return 2; } virtual int F3(int) { return 3; } virtual int F2(int*) { return 4; } void FF(char) {} virtual ~VTableOrder() {}; private: int a, b, c; }; int main() { VTableOrder* p = new VTableOrder(); return 0; }
在类的声明顺序中,F2(int*) 排在第四个,但用 msvc 编译出来后发现实际位置是在第二个。
观察图中的虚函数地址,可以看到虽然顺序变了,但是函数的地址还是按声明的顺序从小到大排列的,F2(int*) 仍然排在第四。
如果把 F2(int) 和 F2(int*) 的位置互换,发现它们在虚表中的顺序也换过来了。看起来只是单纯的后进先出,不像是有特定的排序规则,比如按名称粉碎后的字典序排列等。
因此看汇编代码时也需要留意,不能想当然地按函数的声明顺序来对比。