我非常清楚为什么在C中需要函数指针(例如,在使用某些POSIX设施时)。然而,据我所知,您无法发送它们成员函数,因为有“this”参数。但是,如果您已经在使用类和对象,则为什么不使用面向对象的解决方案,如函数对象?
欢迎提供真实世界中必须使用此类函数指针的示例。
更新:感谢大家的答案。不过,我必须说,这些示例都没有让我相信从纯OO角度来看这是一种有效的机制...
在C++中,“函数对象(Functors)”通常指定义了一个带有任意参数和返回值的operator()
的结构体,可以用作真实函数或函数指针的语法替代品。但是,它们的面向对象问题存在许多问题,首要问题是可用性。这需要大量复杂的样板代码。为了像大多数对话框架那样实现良好的信号框架,需要大量继承的混乱。
在这里,实例绑定的函数指针会非常有益(.NET用委托充分展示了这一点)。
然而,C++成员函数指针仍然满足另一个需求。例如,假设您有很多值在列表中,您想执行其中一个方法,比如其print()
方法。此时,指向YourType::size
的函数指针就很有用,因为它可以让您编写这样的代码:
std::for_each(lst.begin(), lst.end(), std::mem_fun(&YourType::print))
class Image {
// avoid duplicating the loop code
void each(void(Image::* callback)(Point)) {
for(int x = 0; x < w; x++)
for(int y = 0; y < h; y++)
callback(Point(x, y));
}
void applyGreyscale() { each(&Image::greyscalePixel); }
void greyscalePixel(Point p) {
Color c = pixels[p];
pixels[p] = Color::fromHsv(0, 0, (c.r() + c.g() + c.b()) / 3);
}
void applyInvert() { each(&Image::invertPixel); }
void invertPixel(Point p) {
Color c = pixels[p];
pixels[p] = Color::fromRgb(255 - c.r(), 255 - r.g(), 255 - r.b());
}
};
我曾在商业绘画应用程序中看到过它的使用。(有趣的是,这是为数不多的最好用预处理器解决的C++问题之一)。
然而,如今成员函数指针的唯一用途是在boost::bind
的实现中使用。
这里有一个我们常见的典型场景。我们有一个通知框架,在这个框架中,一个类可以注册到多个不同的通知上。在注册通知时,我们会传递成员函数指针。这与C#的事件非常相似。
class MyClass
{
MyClass()
{
NotificationMgr::Register( FunctionPtr( this, OnNotification ) );
}
~MyClass()
{
NotificationMgr::UnRegister( FunctionPtr( this, OnNotification ) );
}
void OnNotification( ... )
{
// handle notification
}
};
我目前正在处理一些代码,其中我使用它们来实现状态机。解除引用的成员函数实现各个状态,但由于它们都在类中,所以它们可以共享整个状态机的全局数据。如果使用普通(非成员)函数指针,则这将很难实现。
然而,我仍然未决定是否这是实现状态机的好方法。
class Processor
{
public:
void operation( int value );
void another_operation( int value );
};
int main()
{
Processor tc;
boost::thread thr1( boost::bind( &Processor::operation, &tc, 100 ) );
boost::thread thr2( boost::bind( &Processor::another_operation, &tc, 5 ) );
thr1.join();
thr2.join();
}
您可以看到创建一个线程并在给定类的实例上执行给定操作的简单性。
上述问题的简单手工方法是创建自己的函数对象:
class functor1
{
public:
functor1( Processor& o, int v ) : o_(o), v_(v) {}
void operator()() {
o_.operation( v_ ); // [1]
}
private:
Processor& o_;
int v_;
};
对于您想要调用的每个成员函数,创建一个不同的函数对象。请注意,operation和another_operation使用的函数对象是完全相同的,在[1]中的调用必须在两个函数对象中都复制一遍。通过使用成员函数指针,您可以编写一个简单的函数对象:
class functor
{
public:
functor( void (*Processor::member)(int), Processor& p, int value )
: member_( member ), processor_(p), value_( value ) {}
void operator()() {
p.*member(value_);
}
private:
void (*Processor::member_)(int);
Processor& processor_;
int value;
};
并使用它:
int main() {
Processor p;
boost::thread thr1( functor( &Processor::operation, p, 100 ) );
boost::thread thr2( functor( &Processor::another_operation, p, 5 ) );
thr1.join();
thr2.join();
}
然而,您甚至不需要定义该函数对象,因为boost::bind已经为您完成了。即将发布的标准将会有自己的版本的bind,沿用boost的实现方式。
成员函数指针是与对象无关的。如果您想在运行时(或作为模板参数)按值引用函数,则需要它。当您没有特定的对象可调用它时,它就会发挥作用。
因此,如果您知道函数,但不知道对象,并且希望通过值传递此知识,则成员函数指针是唯一的规定解决方案。Iraimbilanja的示例很好地说明了这一点。它可能有助于您查看我的成员变量示例用法。原理是相同的。
这就像使用lambda表达式一样。你总是可以将所有必要的本地变量传递给一个简单的函数,但有时你需要传递多个变量。
因此,使用成员函数可以避免将所有必要的成员字段传递给一个函数对象。就是这样。
你特别询问了成员函数,但是函数指针还有其他用途。在C++中,我需要使用函数指针的最常见原因是想要在运行时使用LoadLibrary()加载DLL。这显然是在Windows中。在使用可选DLL形式的插件的应用程序中,由于DLL通常不存在,并且使用延迟加载很麻烦,因此无法在应用程序启动时使用动态链接。
加载库之后,您必须获取要使用的函数的指针。
我已经使用成员函数指针来解析文件。根据在文件中找到的特定字符串,在映射中找到相同的值并调用相关函数。这代替了一个大型的if..else if..else语句来比较字符串。
在一个场景中,我需要向第三方API对象提供一个回调函数指针,并且该回调函数具有预定义的参数列表(因此无法传递任意参数),我使用了成员函数的函数指针。
由于回调函数要处理基于对象状态的传入事件,而这个对象又使用了第三方API触发了回调,所以我无法在全局命名空间中实现回调函数。
因此,我希望回调函数的实现是在使用第三方对象的类的一部分。我在我想要实现回调的类中声明了一个公共静态成员函数,并将其指针传递给API对象(static
关键字避免了this
指针的麻烦)。
我的对象的this
指针随后作为回调的Refcon的一部分传递(幸运的是,它包含了通用的void*
)。 然后,虚拟函数的实现使用传递的指针来调用包含在类中的实际的私有实现的回调。
代码大概是这样的:
public:
void SomeClass::DummyCallback( void* pRefCon ) [ static ]
{
reinterpret_cast<SomeClassT*>(pRefCon)->Callback();
}
private:
void class SomeClass::Callback() [ static ]
{
// some code ...
}