假设一个虚函数在派生类对象上调用(通常在对象上或通过指针/引用),它覆盖了该函数,但它的派生类都没有覆盖它。它是通过 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();
最佳答案
Does the standard say anything about this?
没有。调用是否使用动态调度机制是不可观察的行为。该标准只关注可观察到的行为。
有多少编译器“去虚拟化”虚拟调用最终是实现定义的。如果您只有 T t;
并且执行了 t.whatever()
,那么您不应使用无法将其去虚拟化的编译器。
内联也会影响去虚拟化。鉴于 T t
声明,如果您向函数传递对此对象的引用,并且它采用 T&
参数,则如果该函数被内联,则可以将对它的调用去虚拟化。
但如果它是该函数的非内联实例(例如,指向该函数的指针,可能通过 std::function
或其他),那么去虚拟化就困难得多。你看,编译器看不到整个程序,所以它不能看你是否有某个类继承自 T
并覆盖了那个方法。
只有链接器,通过整个程序的优化,才有可能看到所有继承自它的类定义。即便如此……也许不会。因为 DLL/SO 从类继承在技术上仍然是可能的。并且非内联函数应该能够获取那些 T&
并调用它们的重写方法。
因此,一旦您离开内联链,其中对象的动态类型和对它的虚拟调用对编译器都是可见的,去虚拟化即使不是不可能,也会变得更加困难。
关于C++ 虚函数是正常调用还是虚拟调用?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47344500/