c++虚表查询

C++ vtable query

本文关键字:查询 虚表 c++      更新时间:2023-10-16

我有一个关于这里提供的解释的查询http://www.parashift.com/c++-faq/virtual-functions.html#faq-20.4

在示例代码中,函数mycode(Base *p)调用virt3方法为p->virt3()。在这里,编译器如何确切地知道virt3在vtable的第三个槽中找到?

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

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


长话短说(从@David Rodríguez的评论中获得灵感):它知道在哪里,因为他之前就决定了


1. 该标准没有强制要求任何特定的算法(实际上,它没有说明c++ ABI应该如何实现),但是有几个广泛的c++ ABI规范,特别是Windows上的COM ABI和Linux上的Itanium ABI(以及一般的gcc)。显然,给定相同的类定义,算法必须每次都给出相同的虚参表布局,否则不可能将不同的对象模块链接在一起。

虚值表的布局由Itanium c++ ABI指定,包括GCC在内的许多编译器遵循。编译器本身并不决定函数指针的去向(尽管我认为它确实决定遵守ABI!)。

虚表中虚函数指针的顺序与类中相应成员函数的声明顺序一致。

(例子)。

COM本;Visual Studio —还会按源代码顺序发出虚函数表指针(尽管我找不到规范文档来证明这一点)。

而且,因为函数名在运行时甚至不存在(而是一个函数指针),所以在编译时的虚函数表布局并不重要。函数调用转换的工作方式与普通函数调用转换的工作方式完全相同:编译器已经将函数名称映射到其内部机制中的地址。唯一的区别是这里的映射是指向虚函数表中的一个位置,而不是指向实际函数代码的开头。

在某种程度上,这也解决了您对互操作性的关注。

请记住,这些都是实现机制,c++本身甚至不知道虚表的存在。

编译器有一个定义良好的算法来分配虚函数表中的条目,以便无论处理哪个翻译单元,条目的顺序始终是相同的。在编译器内部,函数名和它们在虚值表中的位置之间有一个映射,这样编译器就可以在函数调用和虚值表索引之间进行正确的转换。

因此,更改带有虚函数的类的定义时,必须重新编译依赖于该类的所有源文件,否则可能会发生不好的事情。