为什么在C ++中使用函数指针到成员方法?

时间:2009-03-17 15:52:50

标签: c++ function-pointers

很多C ++书籍和教程都解释了如何做到这一点,但我还没有看到一个让人有理由选择这样做的书籍。

我非常理解为什么在C中需要函数指针(例如,在使用某些POSIX工具时)。但是,由于“this”参数,AFAIK无法向其发送成员函数。但是如果你已经在使用类和对象,为什么不使用像functor这样的面向对象的解决方案?

您必须使用此类函数指针的真实世界示例将不胜感激。

更新:感谢大家的回答。不过,我不得不说,这些例子都不能说服我从纯粹的OO角度来看这是一个有效的机制......

10 个答案:

答案 0 :(得分:13)

Functors不是面向对象的先验(在C ++中,术语“functor”通常是指定义具有任意参数的operator ()的结构,并且返回值可以用作实际函数的语法插入替换或函数指针)。然而,他们面向对象的问题有很多问题,首先是可用性。它只是一大堆复杂的样板代码。为了在大多数对话框架中建立一个像样的信号框架,需要进行大量的继承混乱。

实例绑定函数指针在这里非常有用(.NET通过委托充分证明了这一点。)

但是,C ++成员函数指针仍然满足了另一个需求。例如,想象一下,您在要执行一个方法的列表中有很多值,比如它print()。指向YourType::size的函数指针在这里有帮助,因为它允许您编写这样的代码:

std::for_each(lst.begin(), lst.end(), std::mem_fun(&YourType::print))

答案 1 :(得分:3)

过去,成员函数指针曾经在以下场景中有用:

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的实现中。

答案 2 :(得分:2)

这是我们这里的典型场景。我们有一个通知框架,一个类可以注册到多个不同的通知。注册通知时,我们传递成员函数指针。这实际上与C#事件非常相似。

class MyClass
{
    MyClass()
    {
        NotificationMgr::Register( FunctionPtr( this, OnNotification ) );
    }
    ~MyClass()
    {
        NotificationMgr::UnRegister( FunctionPtr( this, OnNotification ) );
    }

    void OnNotification( ... )
    {
        // handle notification
    }
};

答案 3 :(得分:2)

我现在正在使用一些代码,我用它来实现状态机。解除引用的成员函数实现状态,但由于它们都在类中,因此它们共享整个状态机的全局数据。使用普通(非成员)函数指针很难完成。

我仍然未决定是否这是实现状态机的好方法。

答案 4 :(得分:0)

就像使用lambdas一样。您总是可以将所有必要的局部变量传递给一个简单的函数,但有时您必须传递多个函数。

因此,使用成员函数可以避免将所有必需的成员字段传递给仿函数。就这样。

答案 5 :(得分:0)

您特别询问了成员函数,但函数指针也有其他用途。我需要在C ++中使用函数指针的最常见原因是我想使用LoadLibrary()加载DLL运行时。显然,这是在Windows中。在使用可选DLL形式的插件的应用程序中,动态链接不能在应用程序启动时使用,因为DLL通常不存在,并且使用delayload是一种痛苦。

加载库后,您必须获得指向要使用的函数的指针。

答案 6 :(得分:0)

我使用了解析文件的成员函数指针。根据文件中找到的特定字符串,在地图中找到相同的值,并调用相关的函数。这是一个比较字符串的大型if..else if..else语句。

答案 7 :(得分:0)

成员指针最重要的用途是创建仿函数。好消息是你甚至不需要直接使用它,因为它已经在库中解决为boost :: bind,但你必须将指针传递给那些库。

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_;
};

为您要调用的每个成员函数创建一个不同的函数。请注意,仿函数与操作 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会为您执行此操作。即将推出的标准将按照boost的实现方式拥有自己的绑定版本。

答案 8 :(得分:0)

指向成员函数的指针与对象无关。如果要在运行时通过值引用函数(或作为模板参数),则需要它。当你没有一个对象可以称之为它时,它就会自成一体。

因此,如果您知道该函数,但不知道该对象并且您希望通过值传递此知识,则point-to-member-function是唯一的规定的解决方案。 Iraimbilanja的例子说明了这一点。它可能会帮助您查看my example use of a member variable。原理是一样的。

答案 9 :(得分:-1)

我在一个场景中使用了一个函数指针指向一个成员函数,我必须提供一个函数指针,该函数指针指向一个带有预定义参数列表的回调(因此我无法将任意参数传递给某些第三方API对象)。

我无法在全局命名空间中实现回调,因为它应该根据使用触发回调的第三方API的对象状态来处理传入事件。

所以我希望回调的实现成为使用第三方对象的类的一部分。我所做的是,我在类中声明了一个公共和静态成员函数,我希望在其中实现回调,并将指针传递给API对象(static关键字保留this指针麻烦)。

然后我的对象的this指针将作为回调的一部分传递给回调(幸运的是它包含一个通用的void*)。 然后,dummy的实现使用传递的指针来调用class =)中包含的回调的实际和私有实现。

它看起来像这样:

public:
    void SomeClass::DummyCallback( void* pRefCon ) [ static ]
    {
        reinterpret_cast<SomeClassT*>(pRefCon)->Callback();
    }
private:
    void class SomeClass::Callback() [ static ]
    {
        // some code ...
    }