假设我有这种层次结构:
class Super
{
public:
virtual void bar();
};
class Sub : public Super
{
public:
virtual void bar() override;
};
尽管使用虚拟关键词,我还有办法避免使用vtable吗? (好奇心)我已经阅读了一些关于编译器优化的内容,当在编译期间知道对象时消除了vtable,我不确定,一直在谷歌周围挖掘,但无法找到任何答案,这是什么意思?
Sub sb;
sb.bar(); //avoids vtable?
Super& sr = sb;
sr.bar(); //avoids vtable?
Super* srp = &sb;
srp->bar(); //avoids vtable?
答案 0 :(得分:6)
其中一位gcc开发人员拥有整个series of blog posts about devirtualization。我认为他也很积极参与SO,所以他可能会有回应。
然而,虚拟化主要通过分析程序流和可能的类型来消除虚拟调度。我认为它一般不会删除虚拟表,但是在第二篇文章中有一个示例,其中虚拟内联,然后可以在编译时通过持续传播完全评估。在这种情况下,编译器/链接器将程序转换为根本不使用该类,因此它不应包含任何对象或vtable。
答案 1 :(得分:2)
Sub sb;
sb.bar(); //avoids vtable?
以上将永远不需要使用vtable进行分派,因为运行时类型是已知的(即,它已知与编译时/静态类型相同,即Sub
)。
Super& sr = sb;
sr.bar(); //avoids vtable?
Super* srp = &sb;
srp->bar(); //avoids vtable?
在这些情况下,如果指针/引用和用法出现在同一个函数中,优化器可能足够聪明以避免通过vtable进行调度。如果将指针或引用传递给其他一些使用其他类型的指针调用migth的外联函数,则通常需要基于vtable的调度。
更一般地说,C ++标准没有对如何实现运行时多态性做出任何规定,因此没有保证的,可移植的方式来消除" vtables"。
那就是说,你最好尽量减少使用vtable进行派遣是:
在实际不需要进一步覆盖的自由时标记覆盖final
,
保持实现内联(即使隐式 - 通过在类定义中使用函数实现)
要查看是否有/两者都有帮助,您必须试验或阅读您自己的编译器/工具链,优化标记等文档。
链接器可能会删除未使用的vtable,也可能不会删除:如果您有多个转换单元,可能需要尝试使用跨对象链接器优化标记。