派生类调用时的虚函数性能?

Agr*_*hak 6 c++ performance virtual function derived-class

从编译时已知为派生类的类调用虚方法时是否存在性能损失?下面我force_speak用派生类显式调用.

码:

#include <iostream>
#include <array>
#include <memory>

class Base
{
public:
  virtual void speak()
  {
    std::cout << "base" << std::endl;
  }
};

class Derived1 : public Base
{
public:
  void speak()
  {
    std::cout << "derived 1" << std::endl;
  }
};

template<class B>
void force_speak(std::array<std::unique_ptr<B>, 3>& arr)
{
  for (auto& b: arr)
  {
    b->speak();
  }
}

int main()
{
  std::array<std::unique_ptr<Derived1>, 3> arr = 
    {
      std::unique_ptr<Derived1>(new Derived1),
      std::unique_ptr<Derived1>(new Derived1),
      std::unique_ptr<Derived1>(new Derived1)
    };
  force_speak(arr);
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

Jon*_*ely 6

从编译时已知为派生类的类调用虚方法时是否存在性能损失?见下面的代码.

这取决于.大多数编译器会像这样"去虚拟化"代码:

Derived1 d;
d.speak();
Run Code Online (Sandbox Code Playgroud)

对象的动态类型在调用站点是已知的,因此编译器可以避免通过vtable进行调用,并且可以直接调用Derived1::speak().

在您的示例中,编译器需要更智能,因为在force_speak您只有 Derived1*指针(存储在unique_ptr对象中)并且在该上下文中,不清楚指向对象的动态类型是否是Derived1更多派生类型.编译器需要内联调用force_speakinto main(动态类型已知的地方)或使用有关类型的一些其他知识来允许进行虚拟化.(作为附加知识的一个示例,整个程序优化可以确定在程序中的任何地方没有声明其他派生类型,因此Derived1* 必须指向a Derived1.)

使用C++ 11 final关键字可以帮助编译器对某些情况进行虚拟化,例如,如果Derived1标记了,final则编译器知道a Derived1*只能指向a Derived1而不是指向可能覆盖的某个其他类型speak()

  • 看看程序集,看起来GCC和Clang都没有完全虚拟化OP代码中的调用.(GCC做了一些聪明的事情,它内联了`Derived1 :: speak()`并对vtable指针进行了比较,只有在比较不相等的情况下跳转到虚拟调用.)在任一类或函数上的`final`使其成为虚拟化的. (4认同)