2

我有一个函数需要在循环中多次调用虚方法,并希望有一种方法可以避免每次都进行 vtable 查找的开销。我想也许获得一个指向该方法的指针是解决这个问题的好方法。下面的代码显示了我正在尝试做的事情,问题是方法的地址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 编译并进行了全面优化。

4

4 回答 4

4

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

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

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

于 2012-06-26T20:26:29.033 回答
2

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

于 2012-06-26T20:16:25.323 回答
0

如果您确定 for 循环中的对象是 a ,最简单的方法是强制转换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. 如果您只能保证该类是 a Base,那么您在上面的问题中编写的内容本质上是一个 vtable,除了比编译器生成的任何东西都要慢得多。您可能听说过 vtables 很慢,与原始 C 指针和函数相比也许确实如此,但我几乎可以完全保证它足够快以满足您的需要。

于 2012-06-26T20:25:23.020 回答
0

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

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

于 2012-06-26T20:14:37.167 回答