理论上,C ++没有二进制接口,并且vtable中的方法顺序是未定义的。改变关于类定义的任何内容,你需要在每个dll等中重新编译依赖于它的每个类。
但我想知道的是编译器如何在实践中 。我希望他们只是使用在头/类中定义方法的顺序,这将使附加方法安全。但他们也可以使用受损名称的哈希来使它们独立,但也完全不可升级。
如果人们具体了解特定编译器的特定版本如何在不同的操作系统等中工作,那么这将是最有帮助的。
补充:理想情况下,将为虚拟方法偏移创建链接器符号,以便永远不会将偏移硬编译到调用函数中。但我的理解是,这从未完成。正确的吗?
答案 0 :(得分:2)
在MSVC 2010中,它们按照您声明的顺序排列。我想不出另一个编译器做任何不同的理由,尽管它是一个随意的选择。它只需要保持一致。它们只是指针数组,所以不要担心散列或变形。
无论顺序如何,派生类中添加的其他虚函数必须在基类或多态转换中的那些函数不起作用之后才会出现。
答案 1 :(得分:2)
似乎微软可以对VTable进行重新排序。
从https://marc.info/?l=kde-core-devel&m=139744177410091&w=2
复制以下内容我(Nicolas Alvarez)可以证实这种行为发生了。
我编译了这个类:
struct Testobj {
virtual void func1();
virtual void func2();
virtual void func3();
};
一个调用func1()的程序; FUNC2(); FUNC3();
然后我在 end :
中添加了一个func2(int)重载struct Testobj {
virtual void func1();
virtual void func2();
virtual void func3();
virtual void func2(int);
};
并重新编译了类,但没有使用该类重新编译程序。
调用func1()的输出; FUNC2(); FUNC3();是
This is func1
This is func2 taking int
This is func2
这表明如果我声明func1()func2()func3()func2(int), vtable的布局为func1()func2(int)func2()func3()。
使用MSVC2010进行测试。
答案 2 :(得分:1)
据我所知,他们总是按照声明的顺序。这样,您始终可以在最后添加的新虚拟方法声明(或者在虚拟方法的所有先前声明之下)。如果删除任何虚拟方法或在中间某处添加新方法 - 您需要重新编译并重新链接所有内容。 我知道这肯定 - 我已经犯了那个错误。根据我的经验,这些规则适用于MSVC和GCC。
答案 3 :(得分:0)
任何编译器必须至少将特定类的所有可行条目放在一起,派生类的条目可以在之前或之后,也可以放在一起。
实现这一目标的最简单方法是使用标题顺序。很难理解为什么任何编译器会做任何不同的事情,因为它需要更多代码,更多测试等,并且只是提供了另一种错误发生方式。我无法看到可识别的好处。