【问题标题】:C++ Virtual function called normally or virtually?C++ 虚函数正常调用还是虚拟调用?
【发布时间】:2025-12-22 13:40:17
【问题描述】:

假设在一个派生类对象上调用了一个虚函数(通常在对象上或通过指针/引用),它覆盖了该函数,但它的派生类都没有覆盖它。它是通过 v 指针“虚拟地”调用还是作为普通函数调用,所以所有优化/内联都适用于它?标准对此有何规定?

class CBase{

public:
    virtual void doSomething() = 0;

};

class CDerived1 : public CBase{

public:
    void doSomething() override { /* do stuff */};

};

class CDerived2 : public CDerived1{


};

//...

CDerived1   derived1;
CDerived1*  p_derived1 = &derived1;

p_derived1->doSomething();

【问题讨论】:

  • 请用一些示例代码说明。也许这样您所考虑的案例的差异就会变得明显。
  • 虚拟是虚拟的,内联需要编译器非常聪明/积极并且了解整个程序。你使用派生两次?你的意思是衍生自衍生?该标准以“虚拟函数支持动态绑定和面向对象编程”的虚拟函数开头。这是编译运行后发生的情况。
  • @Yunnosch 编辑完成

标签: c++


【解决方案1】:

标准对此有任何说明吗?

没有。调用是否使用动态调度机制不是可观察的行为。而且该标准只关注可观察的行为。

编译器“去虚拟化”虚拟调用的多少最终是由实现定义的。如果你只有T t; 而你有t.whatever(),那么你不应该使用不能去虚拟化它的编译器。

内联也会影响去虚拟化。给定T t 声明,如果您向函数传递对此对象的引用,并且它接受T& 参数,则如果该函数被内联,则可以对它的调用进行去虚拟化。

但如果它是该函数的非内联实例(例如,指向该函数的指针,可能通过std::function 或其他方式),那么去虚拟化要困难得多。看,编译器看不到整个程序,所以它无法查看您是否有某个类继承自 T 并覆盖了该方法。

只有链接器,通过整个程序优化,才能看到所有继承自它的类定义。即便如此……也许不是。因为 DLL/SO 从类继承在技术上仍然是可能的。并且非内联函数应该能够接受那些T&s 并调用它们的重写方法。

因此,一旦您离开内联链,其中对象的动态类型和对它的虚拟调用都对编译器可见,去虚拟化即使不是不可能也变得更加困难。

【讨论】: