【问题标题】:Multiple inheritance, virtual functions, and the virtual table in C++C++中的多重继承、虚函数和虚表
【发布时间】:2014-07-13 22:23:49
【问题描述】:

我知道虚函数的 vtable 查找比直接调用函数要慢很多,因为基类必须搜索 vtable 以获取派生函数。如果有更多的派生层,我在徘徊是否会更慢。基本上,我的问题如下:

Base->Derived(1层继承)虚函数调用速度比 Base->Derived1->Derived2->Derived3->DerivedEtc(多个层)虚函数调用?

【问题讨论】:

  • 如果继承是静态的,即非虚拟的,则没有搜索。动态分派是单表查找。使用虚拟继承,您只需添加一个额外的间接步骤。
  • “我知道查找虚函数的 vtable 比直接调用函数要慢很多,因为基类必须搜索 vtable 以获取派生函数。”根据TR Performance,虚函数调用通常不会比普通函数调用慢多少。你付出的代价主要是你不能内联这个函数调用。 “想要性能?衡量。”
  • 简短答案:否。长答案:*.com/questions/5868431/… 另外请注意,从长答案中可以看出,表格不必“搜索”。这是在编译时已知的索引查找。
  • 继承的层数无关紧要。只有最派生的函数地址会放在 vtable 中,因此访问速度同样快。请注意,初始化期间的构造函数或析构函数不一定是这种情况。

标签: c++ inheritance vtable


【解决方案1】:

在两级继承派生类而不是 40 级继承派生类的情况下,单次间接查找以查找要调用的虚函数的性能没有差异。

原因如下:每个类都有一个指向虚拟表的指针,用于解析需要为该特定对象调用哪个函数:

class Base
{
public:
    virtual void function1() {};
    virtual void function2() {};
};

class D1: public Base
{
public:
    virtual void function1() {};
};

class D2: public Base
{
public:
    virtual void function2() {};
};

上面的代码生成了三个虚拟表:一个用于Base类的对象,一个用于D1类的对象,一个用于D2类的对象。

这里重要的一点是您不需要从底层遍历所有虚拟表来查找要为特定对象调用哪个函数

在上图中你只是

  1. 按照您的对象的vptr(一个间接)
  2. 调用你感兴趣的函数的地址

现代编译器能够在我上面列出的两点上优化这个过程,除了一些特殊情况,这不会明显影响性能。

致谢:http://www.learncpp.com/cpp-tutorial/125-the-virtual-table/


额外:正如 dyp 所指出的,虚函数的“性能缓慢”通常是指这些函数由于 vtable 间接而无法内联。这是否重要归结为您正在处理的代码和架构(例如注册压力和其他因素)。

【讨论】:

  • 太棒了!关于“性能缓慢”,我想指出虚拟表中的查找实际上是在编译时确定的固定偏移量。对于 CPU 性能,与直接调用相比,它只是间接调用的成本,需要多一次内存访问+一次加法。性能只会受到轻微影响。