【问题标题】:Calling a virtual function from a derived pointer without paying the vtable price从派生指针调用虚函数而不支付 vtable 价格
【发布时间】:2017-09-29 09:07:08
【问题描述】:

考虑这个简单的例子:

class Base {
    virtual void foo() {};  
};

class Derived: public Base {
    void foo() {}; 
}; 

Base    *b = new Derived;
Derived *d = new Derived;
b->foo();
d->foo();

我的问题是:通过派生类指针调用基类(但不是派生类)中声明为虚拟的成员函数是否使用(并为此付出代价)vtable 机制?例子中b->foo()使用vtable机制调用Derived::foo(),但是d->foo()?

如果是,如何规避这种行为:当显式使用Derived指针时,我想直接调用Derived::foo()方法而不支付vtable的成本,就好像基类不存在一样?

【问题讨论】:

  • 您的实际问题是什么? 为什么你想回避 vtable 查找?您是否测量过这是您应用程序中的一个严重瓶颈?这不仅仅是因为你听到或读过的东西而过早优化?
  • 如果您想确保从Derived 继承的类不会覆盖foo,您可以使用final 关键字。如果这样做,编译器可能会在对 Derived* 调用 foo() 时优化 vtable 查找。
  • 我想过这个final关键字,但是如何检查优化器是否真的跳过了vtable?
  • 编译器尝试优化虚拟调度并尽可能删除间接。 Gcc 在这个方向上已经有了很大的改进。您可以在其中一位 gcc 开发人员的一系列精彩文章中阅读到很多关于它的信息:hubicka.blogspot.de/2014/01/devirtualization-in-c-part-1.html
  • "我想过这个final关键字,但是如何检查优化器是否真的跳过了vtable?"查看为此生成的汇编代码。

标签: c++ performance polymorphism vtable


【解决方案1】:

语法

d->Derived::foo();

将抑制虚拟调度并保证函数Derived::foo 是被调用的函数,即使存在覆盖函数。

这很少是您想要的,但除非您解释为什么要这样做,否则我无法建议替代解决方案。例如,在给出的代码 sn-p 中,根本没有理由动态分配 Derived。你可以直接写这个:

Derived d;
d.foo();

这里,编译器肯定知道动态类型是Derived,所以不需要虚拟调度。

【讨论】:

    【解决方案2】:

    调用基类中声明为 virtual 的成员函数(但不在派生类中)...

    我已经停止阅读这里,因为如果Basefoo 声明为虚拟,Derived 只能覆盖fooDerived::foo(据说是签名匹配)隐式为virtual

    Derived::foo 的以下定义相同

    class Derived: public Base {
      void foo() {}; 
    };
    
    class Derived: public Base {
      virtual void foo() {}; 
    };
    

    这种机制称为隐式虚拟传播,this SO answer tries and explain why id was conceived that way。原因如下:

    10.3 虚函数

    2 如果在类Base和类Derived中声明了一个虚成员函数vf,则直接或间接派生自Base,则成员函数vf同名,参数- type-list (8.3.5)、cv-qualification 和 ref-qualifier(或不存在相同的)作为 Base::vf 被声明,然后 Derived::vf 也是虚拟的(无论是否如此声明) 并覆盖Base::vf

    【讨论】:

    • 好的,我现在的问题是:如何规避?
    • @janou195 您使用布赖恩回答中显示的技巧绕过它。但这将不再尊重预期的语义和开放/封闭原则:如果d 将指向具有自己特殊功能实现的class TwiceDerived : public Derived 的对象,您将忽略此专门化并仍然调用Derived::foo()。这迟早会造成破坏(例如,如果Derived::foo()TwiceDerived::foo() 使用不同的假设来向下转换其他类型的成员指针)。如果您非常关心性能,请改用非虚拟成员
    猜你喜欢
    • 2015-10-24
    • 1970-01-01
    • 1970-01-01
    • 2013-11-04
    • 1970-01-01
    • 2011-04-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多