【问题标题】:Obtain pointer to virtual method?获取指向虚方法的指针?
【发布时间】:2012-06-26 19:53:43
【问题描述】:

我有一个函数需要在循环中多次调用虚方法,并希望有一种方法可以避免每次查找虚表的开销。我想也许获得一个指向该方法的指针是解决这个问题的好方法。下面的代码显示了我正在尝试做的事情,问题是Derived 中的方法地址无法分配给Base 成员函数指针。

class Base
{
    public:

    typedef float ( Base::*MFP )( float const & x ) const;

    virtual MFP getMFP( void ) const = 0;

    virtual float function( float const & x ) const = 0;
};

class Derived : public Base
{
    public:

    virtual MFP getMFP( void ) const
    {
        return &Derived::function;
    }

    virtual float function( float const & x ) const
    {
        return x * x;
    }
};

class Other
{
    public:

    float calculate( float const & x, Base * pBase ) const
    {
        Base::MFP function = pBase->getMFP();
        return ( ( *pBase ).*( function ) )( x );
    }
};

有没有办法做我想做的事情?


编辑:

对于仍然感兴趣的人,我对我的代码进行了定时测试。结果发现动态调度只使我的计算方法减慢了 0.004%,所以几乎没有。使用经过全面优化的 MSVC 2010 编译。

【问题讨论】:

  • 您需要进行 vtable 查找以确保调用正确的函数。这就是拥有虚函数的全部意义......
  • 你分析过这段代码吗?是否明确地是 vtable 查找给您带来了性能问题?
  • 你的编译器在某些情况下可以自己做(只做一次查找)。您是否验证了它输出的(优化的)程序集?
  • 我还没有对其进行分析,开销可能不会那么大。这似乎是一个明显的优化,因为对象的类型不会改变,为什么在一次就足够的情况下执行十或二十次 vtable 查找?

标签: c++


【解决方案1】:

您的想法是基于一个错误的假设,即创建此类指针将使 VMT 访问脱离循环(即执行一次,而不是在循环中重复执行)。

在 C++ 语言中,通过“指向成员函数”类型的指针(恰好绑定到 虚拟 成员函数)的指针调用总是在调用时被解析 ,不是在初始化的时候。这意味着即使你创建了这样一个指针,它也不会优化任何东西。 循环中的每个调用仍将执行对 VMT 的全面访问,就像没有任何指针一样。

在 C++ 中,没有办法在初始化时强制这样一个指针指向一个 特定 版本的虚函数。 C++ 语言根本就没有这样的特性。

【讨论】:

  • 我没有考虑过,我只是假设一个成员函数指针,一旦初始化,将包含实际方法的地址。感谢您的深入回答 +1
【解决方案2】:

不要这样做。处理器有很深的管道,编译器几乎肯定已经缓存了函数指针。你可以通过一些努力做到这一点。但我保证在任何不到 10 年的编译器上,这不会有任何区别。

【讨论】:

  • 我希望编译器能够做到这一点,特别是如果我通过引用传递或声明指针常量。
【解决方案3】:

我会避免过早的优化,直到它成为一个问题;然后,只有在你分析了你的代码之后,你才会进行更改。首先争取可维护性和可扩展性,如果您仍然遇到性能问题,请考虑重构。在此示例中,查找的开销非常小。

我宁愿查看可读的代码,也不愿查看性能提升 5% 但需要我一整天才能弄清楚它在做什么的代码。

【讨论】:

    【解决方案4】:

    如果您确定 for 循环中的对象是 Derived,则最简单的方法是强制转换。

    void foo(float);
    Base* pObject;
    //...
    //If you know with certainty that the object is a Derived
    Derived& der = *static_cast<Derived*>(object)
    for(float x : floatContainer)
    {
         foo(der.function(x));
    }
    

    这意味着您将只调用Derived 的vtable,这可能会更快,具体取决于从Derived 派生的类的数量。如果你只能保证这个类是Base,那么你在上面的问题中写的基本上是一个vtable,除了比编译器产生的任何东西都要慢得多。您可能听说过 vtables 很慢,与原始 C 指针和函数相比也许确实如此,但我几乎可以完全保证它足够快以满足您的需求。

    【讨论】:

    • 这会破坏具有多态类型的对象。如果我知道该方法只会使用 Derived 类型,我将传递对 Derived 对象的引用并取消指向基址的指针。
    • 我看不出它如何优化任何东西。在典型的实现中,V 表访问一旦存在,无论继承结构如何,都会有相同的性能损失。多继承配置中可能存在一些性能差异,但在此示例中不会有任何差异。
    猜你喜欢
    • 2014-03-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-20
    • 2013-10-13
    • 1970-01-01
    • 2010-12-05
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多