4

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

我非常理解为什么函数指针在 C 中是必需的(例如,当使用一些 POSIX 工具时)。但是,AFAIK 由于“this”参数,您无法向他们发送成员函数。但是,如果您已经在使用类和对象,为什么不直接使用函子之类的面向对象的解决方案呢?

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

更新:感谢大家的回答。不过,我不得不说,这些例子都没有真正让我相信,从纯 OO 的角度来看,这是一种有效的机制......

4

10 回答 10

13

函子不是先验的面向对象的(在 C++ 中,术语“函子”通常是指定义operator ()带有任意参数和返回值的结构体,可以用作实际函数或函数指针的语法插入替换)。然而,他们的面向对象问题有很多问题,首先是可用性。这只是一大堆复杂的样板代码。为了像大多数对话框架一样拥有一个像样的信令框架,大量的继承混乱变得必要。

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

然而,C++ 成员函数指针仍然满足另一个需要。例如,想象一下,您想要执行一个方法的列表中有很多值,比如说它的print(). 指向此处的函数指针会有所YourType::size帮助,因为它可以让您编写这样的代码:

std::for_each(lst.begin(), lst.end(), std::mem_fun(&YourType::print))
于 2009-03-17T16:00:02.470 回答
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.

于 2009-03-17T16:03:38.250 回答
2

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

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

    void OnNotification( ... )
    {
        // handle notification
    }
};
于 2009-03-17T16:01:45.540 回答
2

我现在正在处理一些代码,我用它们来实现状态机。取消引用的成员函数实现了状态,但由于它们都在类中,它们可以共享一定数量的数据,这些数据对整个状态机都是全局的。使用普通(非成员)函数指针很难做到这一点。

不过,我仍然不确定这是否是实现状态机的好方法。

于 2009-03-17T16:42:45.490 回答
0

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

因此,使用成员函数将使您免于将所有必要的成员字段传递给函子。就这样。

于 2009-03-17T16:00:01.707 回答
0

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

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

于 2009-03-17T16:02:51.357 回答
0

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

于 2009-03-17T17:02:15.927 回答
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_;
};

并为您要调用的每个成员函数创建一个不同的成员函数。请注意,对于operationanother_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 的实现有自己的 bind 版本。

于 2009-03-17T21:14:08.217 回答
0

指向成员函数的指针与对象无关。如果您想在运行时按值(或作为模板参数)引用函数,则需要它。当您没有想到要调用它的单个对象时,它就会自成一体。

因此,如果您知道函数,但不知道对象并且您希望通过值传递此知识,那么点到成员函数是唯一规定的解决方案。Iraimbilanja 的例子很好地说明了这一点。它可以帮助您查看我使用成员变量的示例。原理是一样的。

于 2011-05-20T20:30:32.470 回答
-1

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

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

所以我希望回调的实现成为使用 3rd-party 对象的类的一部分。我所做的是,我在要实现回调的类中声明了一个公共和静态成员函数,并将指向它的指针传递给 API 对象(static关键字让我避免了this指针问题)。

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

它看起来像这样:

public:
    void SomeClass::DummyCallback( void* pRefCon ) [ static ]
    {
        reinterpret_cast<SomeClassT*>(pRefCon)->Callback();
    }
private:
    void class SomeClass::Callback() [ static ]
    {
        // some code ...
    }
于 2009-03-17T16:24:45.453 回答