将函数指针传递给另一个函数

3

如果我想将任何类的非静态成员函数作为按钮的点击函数传递,我该怎么办?这可行吗?如果可以,我需要做什么?例如,在初始化按钮的任何类(此处为EntityToolGUI)中,我希望将其单击操作设置为该类的非静态成员函数(即EntityToolGUI类的非静态成员函数)。

GUIButton.h

typedef void (*ptr2clickFunc)(void);
class GUIButton : public GUIObject {
  private : void (*clickFunc)(void);
  public : void setClickFunction(ptr2clickFunc clickFunc);
};

GUIButton.cpp
void GUIButton::setClickFunction(ptr2clickFunc clickFunc)
{
  this->clickFunc = clickFunc;
}

EntityToolGUI.h
class EntityToolGUI {
  public : EntityToolGUI();
  protected : void addAnimation();
}

EntityToolGUI.cpp
void EntityToolGUI::addAnimation()
{
  cout<<"add animation"<<endl;
}

EntityToolGUI::EntityToolGUI()
{
  ....
  btnAddAnimation->setClickFunction(&EntityToolGUI::addAnimation);
}

我得到了一个错误:没有匹配的函数调用GUIButton :: setClickFunction(void(EntityToolGUI :: *)())

可选项是void GUIButton :: setClickFunction(void(*)())

我该怎么解决?


当将指针传递给C++方法时,您需要进行额外的处理。请参阅http://mdzahidh.wordpress.com/2008/07/16/pointer-to-c-class-methods-or-should-you-call-em-method-pointers/。 - user529758
5个回答

3

大多数(不错的)将函数指针传递的C代码都使用额外的void*参数来将用户上下文传递给函数。在C++中这不太常见(因为存在比函数指针更好的技术),但是如果您由于某些原因被困在使用函数指针中,则可能是适当的选择。

typedef void (*ptr2clickFunc)(void*);
class GUIButton : public GUIObject {
  private : ptr2clickFunc clickFunc;
  private : void * userdata;
  public : void setClickFunction(ptr2clickFunc clickFunc, void* userdata);
};

class Foo
{
  static void do_foo( void * userdata )
  {
    Foo* thisptr = static_cast<Foo*>(userdata);
    thisptr->foo();
  }
  void foo() { ... }
};

int main()
{
   Foo foo;
   GUIButton button;
   button.setClickFunction( &Foo::do_foo, &foo );
   button.click();
}
编辑 如Bartek所指出,如果你需要经常这样做,你可以将静态函数提取到一个模板中-它看起来有点像这样(未经测试,可能有些错误)。
// GUIButton is as before

// Note no static function here
class Foo { void foo(); }

template<typename T, void(T::*FN)() >
void Call( void * data)
{
  static_cast<T*>(data)->*FN();
}

int main()
{
   Foo f;
   GUIButton button;
   button.setClickFunction( &Call<Foo,&Foo::foo>, &f );
   button.click();
}

既然您已经在使用C ++,那么最好是每个类不要有do_foo方法,而将其制作成模板。 - Bartosz Przybylski

1

您不能将指向非静态成员函数的指针作为指向“常规”非成员函数的指针传递。您应该将addAnimation设置为静态,或者将ptr2clickFunc typedef为成员函数指针

请注意,调用成员函数指针与调用函数指针不同,因为您必须提供要调用成员指针的实例。


如果我想传递任意类的非静态成员函数,我该怎么办?这是可能的吗?如果是的话,我需要做什么?例如,无论在哪个类中初始化按钮,我希望将其点击动作设置为该类的非静态成员函数。 - Jeffrey Chen
@JeffreyChen 你不能创建一个能够持有任何类的成员函数的类型,但如果这些类有一个共同的祖先,你可以存储该祖先的成员函数。当然,在这种情况下并不是必要的,因为你可以使用虚函数代替成员指针。 - Sergey Kalinichenko

1

使用boost的解决方案非常优雅,我想知道是否可以使用std库来实现上述功能。 - Jeffrey Chen
然后使用 mem_fun 或 mem_fun_ptr,可以在 http://cplusplus.com/reference/std/functional/mem_fun_ref/ 上找到参考文献。这可能有点困难,但应该能胜任。 - Bartosz Przybylski
如果你的编译器支持C++11,可以查看std::bindstd::function - Some programmer dude

0

addAnimation需要是静态函数。当回调函数以你现在的方式设置时,EntityTollGUI类的对象不会与该函数一起注册。


0

试试这个(C++11):

#include <stdio.h>
#include <stdlib.h>
#include <functional>

class Raiser
{
public:
    std::function<void(int)> ev1, ev2;

    void RaiseEv1()
    {
        if (!ev1._Empty())
            ev1(44);
    }

    void RaiseEv2()
    {
        if (!ev2._Empty())
            ev2(66);
    }
};

class Handler
{
private:
    int id;

    std::function<void(int)> h;

public:
    Handler(int newId)
    {
        id = newId;

        h = [this](int i)
        {
            printf("Handler with id = %d captured event!\n", this->GetId());
        };
    }

    void Hook1(Raiser & raiser)
    {
        raiser.ev1 = h;
    }

    void Hook2(Raiser & raiser)
    {
        raiser.ev2 = h;
    }

    int GetId()
    {
        return id;
    }
};

int main(int argc, char * argv[])
{
    Raiser raiser;
    Handler handler1(1), handler2(2);

    handler1.Hook1(raiser);
    handler2.Hook2(raiser);

    raiser.RaiseEv1();
    raiser.RaiseEv2();

    getchar();
}

据我所知,在不使用语言扩展的情况下,这是C++中事件处理的最大限度。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接