C++ C++;可变查询

C++ C++;可变查询,c++,C++,我对这里的解释有疑问 在示例代码中,函数mycode(Base*p),调用virt3方法作为p->virt3()。在这里,编译器如何确切地知道virt3位于vtable的第三个插槽中?它如何与什么进行比较?当编译器看到Base的定义时,它会根据一些算法1决定其vtable的布局,就继承自Base的方法而言,这些算法对其所有派生类都是通用的(派生类可以添加其他virtual方法,但它们被放在vtable中,放在从Base继承的内容之后) 因此,当编译器看到p->virt3()时,它已经知道,对于从

我对这里的解释有疑问


在示例代码中,函数
mycode(Base*p)
,调用
virt3
方法作为
p->virt3()
。在这里,编译器如何确切地知道virt3位于
vtable
的第三个插槽中?它如何与什么进行比较?

当编译器看到
Base
的定义时,它会根据一些算法1决定其
vtable
的布局,就继承自
Base
的方法而言,这些算法对其所有派生类都是通用的(派生类可以添加其他
virtual
方法,但它们被放在
vtable
中,放在从
Base
继承的内容之后)

因此,当编译器看到
p->virt3()
时,它已经知道,对于从
Base
继承的任何对象,指向正确的
virt3
的指针例如位于
vtable
的第三个插槽中(因为在定义时它就是这样布置
Base
vtable
),因此它可以正确生成虚拟调用的代码


长话短说(灵感来自@David Rodríguez的评论):它知道它的位置,因为他以前就决定了


1、标准不指定任何特定的算法(实际上,它不涉及任何关于C++ AbI应该如何被植入),但是有几个广泛的C++ ABI规范,特别是Windows上的COM ABI和Linux上的ItAuthAB(以及一般用于GCC)。显然,给定相同的类定义,算法每次必须给出相同的vtable布局,否则就不可能将不同的对象模块链接在一起。

,后面跟着许多编译器,包括GCC。编译器本身并不决定函数指针的去向(尽管我认为它确实决定遵守ABI!)

虚表中虚函数指针的顺序是类中相应成员函数的声明顺序

()

Visual Studio使用的COM还按源代码顺序发出vtable指针(尽管我找不到标准文档来证明这一点)

另外,因为函数名在运行时甚至不存在(而是函数指针),vtable在编译时的布局并不重要。函数调用转换的工作方式与普通函数调用转换的工作方式相同:编译器已经将函数名映射到其内部机制中的地址。唯一的区别是,此处的映射是到vtable中的某个位置,而不是到实际功能代码的开头

在某种程度上,这也解决了您对互操作性的担忧


记住,这是所有实现机制,C++本身不知道虚拟表的存在。

< P>编译器有一个定义好的算法来分配VTILE中的条目,以便无论处理哪个翻译单元,条目的顺序总是相同的。编译器是函数名和它们在vtable中的位置之间的映射,因此编译器可以在函数调用和vtable索引之间进行正确的转换


因此,重要的是,更改具有虚拟函数的类的定义会导致重新编译依赖于该类的所有源文件,否则可能会发生不好的情况。

您希望得到哪种编译器的答案?哪种编译器?“vtables”是一个实现细节。+1换句话说:编译器不需要知道,它决定插槽。@David:很好,我正在添加与答案类似的内容。对不起,我还不清楚。当我调用p->virt3时,编译器(或链接器)是如何工作的知道符号virt3是第三个地址吗?因为编译器在定义与
Base
关联的vtable布局时决定了它。一旦他决定了每种类型的vtable布局如何(在他阅读类定义后发生)事实上,C++编译器是一个很难解决的问题,因为标准甚至没有任何关于C++ ABI的建议。但是,或多或少存在标准的C++ ABIS,在Windows上通常使用COM ABI,Linux the Ita上的AbIK。MyC++的ABI已经成为事实上的标准,它们都规定了VTHT必须如何布局。这与我所说的有什么不同?事实上,VTHT的布局由公共标准ABI或一些内部规范规定的与我写的答案无关。@ Matteo:我不同意,但我不确定是否足够。“现在,我的回答中你已经发现了它的措辞;”——“Matteo:实际上,我现在看到了。”戴维:“戴维:我认为这是因为如果你想提供COM对象,你必须遵循微软C++ ABI。