一般来说,在现代操作系统中,顺序不取决于编译器,但可能取决于操作系统。
任何现代操作系统都不仅定义了操作系统 API,而且还定义了许多标准库,使程序可以更轻松地访问该 API。此外,有很多库提供的服务不直接转换为 OS API,而是为其他东西提供通用 API。像 Linux 的 QT 或 Windows 的 MSVC。
如果您是编译器供应商(对于“供应商”的宽松定义,因为今天大多数编译器都是免费的),您希望编译器生成的代码能够与这些标准库交互。如果你试图让人们使用“你的”编译器,但告诉他们他们不能使用操作系统或标准库提供的接口,你将很难说服他们。这意味着,任何生成 Windows 代码的编译器都将确保它符合 MSVC 标准。任何生成 Linux 代码的编译器都将遵守gcc的Linux/BSD 标准。这不仅仅是关于 vtable 的布局,它还包括名称修改、函数参数/返回值的寄存器和堆栈使用、浮点处理以及许多其他内容。
请注意,情况并非总是如此。在 DOS 的后期/Windows 的早期,当 Microsoft C(++) 和 Watcom C(++) 是(最重要的)竞争对手时,它们的 ABI 非常不同。你不能(不经过很多额外的环节)将用 Watcom 编译的程序链接到用 MS 编译的库,但这没什么大不了的,因为编译器无论如何都包含所有库,并且开源库可以被包含在你的程序中是(大部分)闻所未闻的。(当时 Watcom 甚至没有使用 vtables;他们在每次创建新对象时都会实例化每个对象中使用函数指针)。因此,您回到过去越远,同一操作系统的两个编译器共享相同 ABI 的可能性就越小。
在整个操作系统中,您不需要使库兼容,因此在以不同方式处理事物时有更多余地。但是,您通常会在定义 ABI 时做出相同的决定。您的类构造函数通常是第一个 vtable 条目,因为每个类都需要一个构造函数。析构函数通常是第二个条目,因为每个类都需要一个析构函数。其他 vtable 条目通常在 vtable 中的下一个,按照它们在源代码中出现的顺序。不是因为你试图与其他任何事物兼容;只是因为这是实现您需要实现的东西的最简单方法。
这也意味着你不能依赖它。如果有一家新公司,我们称他们为Orange,设计一个带有他们称为 O-IS(Orange Integration System)的操作系统的新设备,他们可能想要推出自己的编译器。也许他们决定,在他们的 ABI 中,方法名称应该在他们的 vtables 中按字母顺序排序。也许他们决定如何处理多重继承和虚函数,这与 MSVC 和 gcc 处理这些事情的方式不同。当 O-IS 出现时,gcc 人员决定他们希望 gcc 能够为 O-IS 进行编译,他们将根据 Orange 为 O-IS 设置的新标准调整他们的代码生成。
有时,公司试图在ABI同意了新的处理器,像这样对命运多舛的安腾。当然,这些是建议,而不是法律,因此操作系统/设备供应商可能会也可能不会选择遵守它们。
因此,要比较不同编译器和操作系统之间的 vtable,您需要检查这些编译器针对这些操作系统的 ABI 定义。
维基百科的条目显示了(部分)gcc如何处理 Linux ABI,虽然我无法找到 MS 关于 MSVC 如何做到这一点的任何官方文档,但 openrce.org 有一篇关于它的文章。