虚拟最终函数(基类final
)是否有任何vtable / virtual成本?
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()
不太安全,因为我仍然可以隐藏父母的功能。
以下链接不提供答案。
答案 0 :(得分:1)
据我所知,未指定任何特定编译器如何实现虚函数机制。如果它至少有一个虚函数,即使这个虚函数被标记为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
类的大小会增加,当您通过对foo
类的指针/引用或从Base
派生的某个类调用Base
时,您需要支付一些通过vtable重定向的额外费用。
答案 1 :(得分:1)
从纯粹的理论角度考虑,不考虑编译器优化,fCheap()
确实是最快的。 fFinal()
和fVirtual()
都会增加查找/间接的成本,因为它们必须通过vtable调用。由于vtable,对象大小也会增加,但这与final
无关。只要您的班级(或其中一个基地)有任何虚拟功能,您就可以支付大小的费用。
输入现代优化器,即虚拟化。增加物体尺寸的成本不会消失。虚拟呼叫的成本可能会。声明一个虚函数并立即使其成为最终版本使编译器在编译时能够相对容易地证明将调用哪个函数。投注是fFinal()
调用将被虚拟化为正常的函数调用[1]。如果FVirtual()
可能发生这种情况,那么完全取决于呼叫站点的情况。
通常,虚拟呼叫开销很小。我不会太担心它,除非你从分析你的实际代码中找到证据表明这是一个性能瓶颈。
<强>更新强>
[1]您赢得赌注的可能性取决于您的编译器,其版本和配置。确保唯一的方法是查看从实际代码生成的程序集。我不敢在这里作一般性陈述。我在Compiler Explorer的-O3
与最近的GCC和Clang进行了一些比赛。在我试过的每个例子中fFinal()
被内联(被授予,它们有点微不足道)。所以,显然,两个编译器都没有问题证明远远超过虚拟性。 :)