替代的虚函数调用实现?

49

C++通过虚拟机制支持动态绑定。但据我所知,虚拟机制是编译器的实现细节,标准只规定了特定情况下应该发生的行为。大多数编译器都通过虚拟表和虚拟指针来实现虚拟机制。这不涉及虚拟指针和表的实现细节。我的问题是:

  1. 是否有任何编译器以除了虚拟指针和虚拟表机制之外的其他方式实现虚拟函数的动态调度?据我所见,大多数编译器(如G++、Microsoft Visual Studio)都是通过虚拟表和指针机制来实现的。那么实际上还存在其他的编译器实现方式吗?
  2. 仅具有一个虚拟函数的任何类的sizeof将是该编译器上指针(在this中的vptr)的大小。因此,鉴于虚拟指针和TBL机制本身是编译器的实现,我上述说法是否总是正确的?

2
@Als...我也对同样的问题思考了很长时间,从未想过发起讨论。感谢你的启动,Als。 - Nawaz
1
如果你想知道在不同编译器生成的模块之间使用接口(虚基类)是否安全,请记住,即使两个编译器都使用虚表,虚表布局也必须匹配。(还有调用约定和其他一些东西。)COM的工作假设给定平台上的所有编译器都可以创建兼容的虚表(对于该平台),尽管我认为这种假设在某些编译器上失败了(或者至少很难满足)。 - Leo Davidson
非常好的问题。我相信可能会有编译器,而不是使用vptr,将整个函数表存储在每个对象中。在某些非常特定的情况下,这可能很有价值,因为在虚拟函数调用期间,这种方法可以节省额外的内存间接引用。在驱动程序开发中,这可能是至关重要的:这种间接引用可能导致对分页内存的访问。 - valdo
@valdo: 然而,在一般情况下,那会浪费很多内存空间。 - Johan Kotlinski
别忘了,如果你启用了RTTI编译... - rwong
@kotlinski:当然。这就是为什么我说在某些非常特定的情况下,这可能是有意义的。 - valdo
11个回答

0

Tony D的回答正确指出编译器可以使用整个程序分析来将虚函数调用替换为对唯一可能的函数实现的静态调用;或者将obj->method()编译成等效的

if (auto frobj = dynamic_cast<FrequentlyOccurringType>(obj)) {
    frobj->FrequentlyOccurringType::method();  // static dispatch on hot path
} else {
    obj->method();  // vtable dispatch on cold path
}

Karel Driesen和Urs Hölzle在1996年撰写了一篇非常有趣的论文,其中模拟了完美的整个程序优化对典型C++应用程序的影响:"C++中虚函数调用的直接成本"。(如果您搜索一下,PDF文件是免费提供的。)不幸的是,他们只对比了vtable分派与完美静态分派;他们没有将其与二叉树分派进行比较。
他们指出,在支持多重继承的语言(如C++)中,实际上有两种vtable。使用多重继承时,当您调用从第二个基类继承的虚方法时,您需要“修正”对象指针,使其指向第二个基类的实例。这个修正偏移量可以作为数据存储在vtable中,也可以作为代码存储在“thunk”中。(有关更多详细信息,请参见该论文。)
我相信现在所有体面的编译器都使用thunks,但是它确实需要10到20年的时间才能达到100%的市场渗透率。

FrequentlyOccurringType 是一个“final”类吗?(“final”类是一个没有被派生重要函数覆盖并需要实例化vtable的类) - curiousguy
@curiousguy: 是的。按照定义,整个程序分析会看到“整个程序”,因此它知道FrequentlyOccurringType是否有任何子类会对我们造成麻烦。在这种情况下,它可以退而使用if (typeid(obj) == typeid(FrequentlyOccurringType)),或者在前面加上if (auto aobj = dynamic_cast<AwkwardChildType>(obj)) aobj->AwkwardChildType::method()。这与跟踪JIT中的“保护”概念相同,只是在编译(链接)时而不是解释时完成。 - Quuxplusone
我相信在几乎所有情况下,if (typeid(obj) == typeid(FrequentlyOccurringType)auto frobj = dynamic_cast<FrequentlyOccurringType>(obj) 更有效率。 - curiousguy
好吧,把这归咎于我五年前的知识水平。 :) 从那时起,我已经看过https://www.youtube.com/watch?v=QzJL-8WbpuU,我同意你的评估。(为自己辩护,我确实说过“相当于...”,这包括编译器可能会优化它的可能性,因为我们假设它知道整个类层次结构。另外,如果你要问“这在DLL中如何工作?”...它不工作。 :)) - Quuxplusone

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接