C++中的多重继承、虚函数和虚表

3
我知道虚函数的vtable查找比直接函数调用慢得多,因为基类必须通过vtable搜索派生函数。我想知道如果有更多派生层级,这个速度是否会变得更慢。我的问题如下:
在继承1层的情况下,Base->Derived(仅有1层派生)的虚函数调用是否比多层派生的Base->Derived1->Derived2->Derived3->DerivedEtc(多层)虚函数调用更快?

3
如果继承是静态的,也就是非虚继承,那么就没有搜索过程。动态分派只需要进行一次表格查找。而使用虚继承,你只需要再添加一个简单的间接步骤。 - Kerrek SB
2
根据TR Performance的说法,虚函数调用通常并不比普通函数调用慢多少。你所付出的代价主要是无法内联此函数调用。“想要提高性能?那就去测量。” - dyp
1
简短回答:不行。长回答请参考:https://dev59.com/BVbUa4cB1Zd3GeqPB8yr。同时请注意,从长回答中可以得知,这个表格不需要被“搜索”。它是在编译时已知的索引查找。 - Drew Dormann
继承的层数并不重要。只有最派生函数的地址将被放置在虚函数表中,因此访问同样快速。请注意,在初始化期间,构造函数或析构函数可能并非如此。 - Rudy Velthuis
1个回答

7

在两级继承的派生类和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间接寻址,这些函数无法内联。无论是否真的有影响,都归结于你正在处理的代码和架构(例如,寄存器压力等因素)。

1
太好了!关于“性能慢”的问题,我想指出的是,虚拟表中的查找实际上是在编译时确定的固定偏移量。对于CPU性能而言,它只是间接调用与直接调用的成本比较,需要一个额外的内存访问+加法。性能应该只会受到轻微的影响。 - Christophe

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接