我有一个需要在循环中多次调用虚方法的函数,并且希望每次都能有一种方法来避免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进行编译并进行全面优化。
答案 0 :(得分:4)
你的想法是基于一个错误的假设,即创建这样的指针会将VMT访问带出循环(即执行一次而不是在循环中重复执行它)。
在C ++语言中,通过“指向成员函数的指针”类型的指针调用(恰好绑定到虚拟成员函数)总是在调用时解析 ,而不是在初始化时。这意味着即使您创建了这样的指针,它也不会优化任何内容。 循环中的每个调用仍将执行对VMT的全面访问,就像没有任何指针一样。
在C ++中,没有办法在初始化时迫使这样的指针指向虚拟函数的特定版本。 C ++语言根本没有这样的功能。
答案 1 :(得分:2)
不要这样做。处理器有深层管道,编译器几乎肯定已经缓存了函数指针。你可以付出一些努力来做到这一点。但我保证,对于任何不到10年的编译器,这都没有区别。
答案 2 :(得分:0)
我会避免过早优化,直到它成为一个问题;然后,只有这样,在您分析了代码之后,才进行更改。首先拍摄可维护性和可扩展性,如果您仍然遇到性能问题,请查看重构。在这个例子中,查找的开销非常小。
我宁愿看一下代码,这些代码比性能提升5%的代码更具可读性,但是我花了一整天的时间来弄清楚它在做什么。
答案 3 :(得分:0)
如果您确定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,除了比编译器产生的任何东西慢得多。您可能听说vtable很慢,也许与原始C指针和函数相比,这是真的,但我可以完全保证它足够快以满足您的需求。