Указатель на функцию-член странным образом - PullRequest
3 голосов
/ 14 октября 2011

У меня есть класс, Mouse, чем обрабатывает события мыши.Он состоит из ряда статических функций для простых вызовов «где это и т. Д.», Но у него также есть несколько нестатических элементов, а именно некоторые средства обработки событий, когда он используется в качестве объекта.Однако у меня возникли проблемы с тем, как я могу разрешить любому объекту подписываться на события.В моем файле 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 ]

3 голосов
/ 14 октября 2011

он не будет жаловаться и будет работать нормально, но проблема в том, что это ограничивает подписчиков на событие только объектами 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 голос
/ 14 октября 2011

Чтобы класс Mouse обрабатывал указатели на функции-члены произвольных классов, вы можете сделать его шаблоном:

template<class T>
class Mouse {
    typedef void (T::*MouseEvent)(Point pos,MouseButton button);
    MouseEvent m_downEvent;
    //...
    void HookMouseDown(MouseEvent handler);
    void OnMouseDown();
}
0 голосов
/ 14 октября 2011

Вы действительно хотите использовать std :: function и std :: bind / lambdas (функция / bind также доступна в Boost).Это более чем адекватно решает проблему.

0 голосов
/ 14 октября 2011

Или это был бы плохой дизайн, позволяющий чему-либо потреблять события мыши?

Я думаю, что было бы неплохо использовать дизайн, основанный на событиях. С другой стороны, в заднице трудно поддерживать обе функции-члена и . Особенно, если вам нужно также предоставить экземпляр, который вызывает эту функцию.

По совпадению я наткнулся на реализацию Delegate, которая как раз и занимается этим вопросом. Взгляните на эту интересную статью .

HookMouseDown теперь может возвращать ссылку на внутренний делегат, который затем может быть привязан либо к свободной функции, либо к функции-члену:

mouse.HookMouseDown().Bind<MyClass, &MyClass::MyMember>(myinstance);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...