指向成员函数的奇怪方式

时间:2011-10-14 09:09:17

标签: c++ design-patterns event-handling function-pointers

我有一个类,鼠标,而不是处理鼠标事件。它包含许多静态函数,用于简单的“where it it,etc”调用,但它也有一些非静态成员,即当它用作对象时的一些事件处理内容。我遇到了麻烦,但我如何允许任何对象订阅事件。在我的Mouse.h文件中,我有以下声明:(原谅语法错误,这是来自内存)

typedef void (*MouseEvent)(Point pos,MouseButton button)

class Mouse {
    MouseEvent m_downEvent;
    //...
    void HookMouseDown(MouseEvent handler);
    void OnMouseDown();
}

......并在实施中......

void Mouse::HookMouseDown(MouseEvent handler) {
    if (handler != NULL) m_downEvent = handler;
}
void Mouse::OnMouseDown() {
    if (m_downEvent != NULL) m_downEvent(m_pos,m_button);
}

现在在我的订阅者代码中,以这种方式连接事件似乎合乎逻辑:

m_mouse.HookMouseDown(&MyClass::MouseDown);

但是我的编译器(MVC2008)并不喜欢这样一个事实,即我将它传递给指向成员函数的指针而不是指向自由指针的函数。经过一些研究,我发现将typedef更改为

typedef void (MyClass::*MouseEvent)(Point pos,MouseButton button)

它不会抱怨并且可以正常工作,但问题是这会将事件的订阅者限制为 MyClass对象。 我是否需要获取模板以允许任何对象订阅这些事件?或者首先允许任何东西消耗鼠标事件是不好的设计?

4 个答案:

答案 0 :(得分:3)

  

它不会抱怨并且可以正常工作,但问题是这会将事件的订阅者限制为只有MyClass对象。

不,您也可以通过派生类实例“调用”该成员。

无论如何,使用std :: mem_fun_ptr(c ++ 03)std :: function<>这个问题已经解决了很多次。 (c ++ 0x),std :: bind(c ++ 0x)和boost :: bind。

以下是完整示例,请参阅直播https://ideone.com/mut9V

#include <iostream>

struct MyBase
{
     virtual void DoStuff(int, float) { std::cout << "Base" << std::endl; } 
};

struct MyDerived : MyBase
{
     virtual void DoStuff(int, float) { std::cout << "Derived" << std::endl; } 
};

int main()
{
    typedef void (MyBase::*memfun)(int, float);

    memfun event(&MyBase::DoStuff);

    MyBase base;
    MyDerived derived;

    (base.*event)(42, 3.14);
    (derived.*event)(42, 3.14);
}

答案 1 :(得分:1)

要使类Mouse处理指向任意类的成员函数的指针,可以将其作为模板:

template<class T>
class Mouse {
    typedef void (T::*MouseEvent)(Point pos,MouseButton button);
    MouseEvent m_downEvent;
    //...
    void HookMouseDown(MouseEvent handler);
    void OnMouseDown();
}

答案 2 :(得分:0)

  

或者首先允许任何东西消耗鼠标事件是不好的设计?

我认为使用基于事件的设计是相当不错的。另一方面,支持自由函数成员函数是一件痛苦的事。特别是如果你还要提供调用这个函数的实例。

巧合的是,我偶然发现了一个Delegate实施,它完全解决了这个问题。看看这个interesting article

HookMouseDown现在可以返回对内部委托的引用,然后可以绑定到自由函数或成员函数:

mouse.HookMouseDown().Bind<MyClass, &MyClass::MyMember>(myinstance);

答案 3 :(得分:0)

你真的想要std :: function和std :: bind / lambdas(函数/绑定在Boost中也可用)。这足以解决问题。