代码之家  ›  专栏  ›  技术社区  ›  Uri

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

  •  4
  • Uri  · 技术社区  · 16 年前

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

    我非常理解为什么C中需要函数指针(例如,在使用一些POSIX工具时)。但是,由于“this”参数的原因,afaik不能向它们发送成员函数。但是,如果您已经在使用类和对象,为什么不使用类似于函数的面向对象的解决方案呢?

    在现实世界中,如果您必须使用这些函数指针,我们将不胜感激。

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

    10 回复  |  直到 14 年前
        1
  •  13
  •   Konrad Rudolph    16 年前

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

    实例绑定函数指针在这里非常有用(.net用委托充分演示了这一点)。

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

    std::for_each(lst.begin(), lst.end(), std::mem_fun(&YourType::print))
    
        2
  •  3
  •   Iraimbilanja    16 年前

    在过去,成员函数指针在类似这样的情况下很有用:

    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 .

        3
  •  2
  •   decasteljau    16 年前

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

    class MyClass
    {
        MyClass()
        {
            NotificationMgr::Register( FunctionPtr( this, OnNotification ) );
        }
        ~MyClass()
        {
            NotificationMgr::UnRegister( FunctionPtr( this, OnNotification ) );
        }
    
        void OnNotification( ... )
        {
            // handle notification
        }
    };
    
        4
  •  2
  •   T.E.D.    16 年前

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

    不过,我还没有决定这是否是实现状态机的一种好方法。

        5
  •  0
  •   Mykola Golubyev    16 年前

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

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

        6
  •  0
  •   John Dibling    16 年前

    您特别询问了成员函数,但函数指针还有其他用途。我需要在C++中使用函数指针的最常见的原因是当我想使用Load RealBug()加载一个DLL AR运行时。这显然是在窗户里。在以可选dll形式使用插件的应用程序中,动态链接不能在应用程序启动时使用,因为dll通常不存在,使用delayoad是一种痛苦。

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

        7
  •  0
  •   void    16 年前

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

        8
  •  0
  •   David Rodríguez - dribeas    16 年前

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

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

    并为要调用的每个成员函数创建一个不同的函数。请注意,函数与 操作 为了 另一个操作 ,但必须在两个函数中复制[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版本。

        9
  •  0
  •   Community CDub    8 年前

    指向成员函数的指针是对象不可知论的。如果要在运行时按值引用函数(或作为模板参数),则需要使用它。当你没有一个单一的对象在脑海中召唤它时,它就形成了自己。

    因此,如果您知道函数,但不知道对象,并且希望通过值传递这些知识,那么指向成员函数是 唯一 规定的解决方案。伊拉姆比兰扎的例子很好地说明了这一点。它可以帮助你看到 my example use of a member variable . 原理是一样的。

        10
  •  -1
  •   Bjoern    16 年前

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

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

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

    这个 然后,我对象的指针将作为回调的refcon的一部分传递(幸运的是,回调包含一个通用的 void* ) 然后,虚拟对象的实现使用传递的指针来调用类()中包含的回调的实际实现和私有实现。

    看起来像这样:

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