基类的虚拟和最终函数的成本(vtable/虚拟成本)

cost of Virtual-and-final function at base class (vtable/virtual-cost)

本文关键字:虚拟 vtable 函数 基类      更新时间:2023-10-16

虚拟最终函数是否有任何虚拟/虚拟成本(final在基类中)?

class B{
public: virtual void fFinal() final{ .... do something  ..... }
public: void fCheap() { .... do something ..... }
public: virtual void fVirtual() { .... do something ..... }
};
class C : public B{
public: virtual void fVirtual() { .... do something ..... }
};

fFinal()的成本是 =fCheap()成本还是fVirtual()成本?

我将使用 final+virtual 来防止人为错误("不要覆盖我")。
fCheap()不是那么安全,因为我仍然可以隐藏父级的功能。

以下链接不提供答案。

  • 最终的虚拟功能有什么意义?
  • 虚函数的成本是否随着继承树中类的数量而增加?
  • http://en.cppreference.com/w/cpp/language/final

据我所知,任何特定的编译器如何实现虚函数机制都没有具体说明。如果类中至少有一个虚函数,即使这个虚函数被标记为 final,他们很可能会将指向 vtable 的指针放入类中。

我已经根据您的代码片段测试了一个简化的示例:

class Base {
public:
virtual void foo() final {}
};
static_assert(sizeof(Base) > 1, "No pointer to vtable");

似乎 gcc 6.3 和 clang 4.0 都添加了指向 vtable 的指针。

这意味着管理通常虚函数的规则适用于在基类中同时标记为虚拟和最终的函数。因此,Base类的大小会增加,并且当您通过指向Base类或从Base派生的某个类的指针/引用调用foo时,您需要为通过 vtable 重定向支付一些额外费用。

从纯粹的理论角度来看,不考虑编译器优化,fCheap()确实是最快的。fFinal()fVirtual()都会产生增加查找/间接寻址的成本,因为它们必须通过 vtable 调用。此外,对象大小也会因为 vtable 而增加,但这与final无关。一旦你的类(或其基础之一)有任何虚函数,你就要支付大小成本。

进入现代优化器,即去虚拟化。增加对象大小的成本不会消失。虚拟呼叫的成本可能会增加。声明一个虚函数并立即使其成为最终函数,使编译器在编译时相对容易地证明将调用哪个函数。投注是fFinal()调用将被解虚拟化为普通函数调用[1]。FVirtual()是否也会发生这种情况,完全取决于呼叫现场的情况。

通常,虚拟呼叫开销很小。我不会太担心它,除非你有来自分析实际代码的有力证据,证明这是一个性能瓶颈。

更新

[1] 您赢得赌注的可能性取决于您的编译器、版本和配置。确保的唯一方法是查看从实际代码生成的程序集。我不敢在这里作一般性发言。我在编译器资源管理器中-O3玩了一下最近的GCC和Clang。在我尝试的每个示例中fFinal()都是内联的(当然,它们有点微不足道)。所以,很明显,两个编译器都没有问题,甚至比虚拟性更能证明。:)