2

在 VTable 的上下文中,虚拟方法调用和直接方法调用有什么区别?在虚拟和直接调用的情况下如何解决方法参考?

4

2 回答 2

3

理论上,不存在这样的东西,C++ 标准没有定义它(定义了虚拟调用,但没有指定它们必须如何工作,不存在 VTable 这样的东西)。

实际上,虚拟调用确实在我所知道的每个编译器上都使用了一个 vtable。这是每个具有虚拟成员(每个类,而不是每个实例!)的类的一个地址表和每个实例一个指向该表的指针的内存开销。
虚拟调用——取决于架构——要么是从该表加载,然后是对加载地址的调用,要么是所谓的间接调用(这是相同的,但在一条指令中)。

直接调用只是对编译器知道的地址的普通函数调用(单指令)(共享库调用存在一些例外,它也可能使用间接甚至双重间接调用)。每当编译器 100% 确定对象的运行时类型时,或者当您通过范围解析 ( operator::) 明确告诉它时,这就是所使用的。

传统上,直接调用和间接调用之间的最大区别在于分支预测和流水线,这使得间接调用更加昂贵(10-15 倍),但最近的 CPU 在这两种情况下都同样出色地实现了这一点(现代 CPU 上有一个专用的间接调用缓存) CPU)。
我不会说差异不存在或可以忽略不计,但现在肯定不是什么大问题。

于 2012-08-20T16:51:12.573 回答
1

定义了虚方法的类将向该类添加一个隐藏指针成员。指针指向所谓的 V 表,它是对应于类的虚方法的函数指针块。

从具有虚拟方法的类派生的每个类都继承此隐藏指针成员。但是,此外,每个这样的类都有自己的 V-table。因此,派生类的实例化会将这个指针初始化为它自己的 V 表。

这样,当使用虚方法调用基类上的虚方法时,当它查询 V 表指针时,该指针实际上指向派生类的表,这就是从基类调用虚方法实际上会调用派生类的实现。因此,当遇到虚拟方法时,编译器会使用此代码解析调用,该代码通过 V 表指针进行查找。

直接方法调用没有 V 表查找代码。编译器只需将调用解析为对应于方法的函数地址。

于 2012-08-20T16:46:52.940 回答