Kev*_*sen 13 c++ performance inheritance vtable
我的理解是,由于两个问题,虚函数可能会导致性能问题:vtable引起的额外derefencing以及编译器无法在多态代码中内联函数.
如果我将变量指针向下转换为其确切类型怎么办?那还有额外的费用吗?
class Base { virtual void foo() = 0; };
class Derived : public Base { void foo() { /* code */} };
int main() {
Base * pbase = new Derived();
pbase->foo(); // Can't inline this and have to go through vtable
Derived * pderived = dynamic_cast<Derived *>(pbase);
pderived->foo(); // Are there any costs due to the virtual method here?
}
Run Code Online (Sandbox Code Playgroud)
我的直觉告诉我,由于我将对象转换为其实际类型,编译器应该能够避免使用虚函数的缺点(例如,它应该能够内联方法调用,如果它想要).它是否正确?
在我转发后,编译器是否真的知道pderived是Derived类型的?在上面的例子中,看到pbase是Derived类型的微不足道,但在实际代码中它可能在编译时是未知的.
既然我已经写下来了,我想由于Derived类本身可以被另一个类继承,将pbase向下转换为Derived指针实际上并不能确保编译器的任何内容,因此它无法避免成本虚拟功能?
Pra*_*han 21
神秘的Sufficiently Smart Compiler可以做什么,以及实际编译器最终做什么之间总是存在差距.在您的示例中,由于没有任何继承Derived
,最新的编译器可能会将调用虚拟化为foo
.但是,由于成功的虚拟化和后续内联通常是一个难题,因此尽可能使用final
关键字帮助编译器.
class Derived : public Base { void foo() final { /* code */} }
Run Code Online (Sandbox Code Playgroud)
现在,编译器知道,只有一个可能foo
,一个Derived*
可以调用.
(有关为什么虚拟化很难以及gcc4.9 +如何处理它的深入讨论,请阅读Jan Hubicka在C++系列文章中的Devirtualization.)
Pradhan的使用建议final
是合理的,如果更改Derived
类是一个选项,你不需要任何进一步的推导.
特定呼叫站点可直接使用的另一个选项是为函数名称添加前缀Derived::
,禁止虚拟调度到任何进一步的覆盖:
#include <iostream>
struct Base { virtual ~Base() { } virtual void foo() = 0; };
struct Derived : public Base
{
void foo() override { std::cout << "Derived\n"; }
};
struct FurtherDerived : public Derived
{
void foo() override { std::cout << "FurtherDerived\n"; }
};
int main()
{
Base* pbase = new FurtherDerived();
pbase->foo(); // Can't inline this and have to go through vtable
if (Derived* pderived = dynamic_cast<Derived *>(pbase))
{
pderived->foo(); // still dispatched to FurtherDerived
pderived->Derived::foo(); // static dispatch to Derived
}
}
Run Code Online (Sandbox Code Playgroud)
输出:
FurtherDerived
FurtherDerived
Derived
Run Code Online (Sandbox Code Playgroud)
这可能很危险:实际的运行时类型可能取决于调用它的覆盖以维持其不变量,因此除非存在紧迫的性能问题,否则使用它是个坏主意.
代码在这里可用.