如何从鼠标处理程序中调用成员功能

How to call a member functions from within a mouse handler?

本文关键字:调用 成员 功能 程序 处理 鼠标      更新时间:2023-10-16

让我这样的课程,例如:

class IWavePlayer : public IControl
{
private:
    WDL_String mWavePath;
public:
    IWavePlayer() { 
        // some task
    }
    void LoadWave() {
        mWavePath = PromptForFile();
        // some task with mWavePath
    }
};

我用 IWavePlayer pWavePlayer;在主机中实现它。

现在,我需要致电(在另一个"处理"鼠标单击的控件中)pWavePlayer的函数LoadWave()

class ICustomButton : public IControl
{
private:

public:
    ICustomButton()  {
        // some task
    }
    void ICustomButton::OnMouseDown(int x, int y, IMouseMod *pMod) {
        pWavePlayer.LoadWave();
    }
};

我无法在此处拨打pWavePlayer.LoadWave();,因为显然它不知道" pWavePlayer。而且我不能将pWavePlayer实例传递给Icustombutton(例如,它仅针对1个按钮)。

您通常如何管理这种情况?这样做的正确模式是什么?

当您需要从另一个对象的无关成员函数上调用对象上的成员函数时,您需要为呼叫者提供引用或指向目标的指针。这通常是在构造函数中完成的:

class ICustomButton : public IControl {
private:
    IWavePlayer *pWavePlayer;
public:
    ICustomButton(IWavePlayer *wp) : pWavePlayer(wp)  {
        // some task
    }
    void ICustomButton::OnMouseDown(int x, int y, IMouseMod *pMod) {
        pWavePlayer->LoadWave();
    }
};

这也可以通过提供某种"定位器服务"或"注册表"来完成,可以通过该指针找到IWavePlayer的指针,而无需提供任何参考。例如,如果系统中只有一个IWavePlayer,则可以制作一个单例,这意味着在整个系统中可以访问它为IWavePlayer::instance

如果您根本不想在ICustomButton中提到IWavePlayer,则可以使用std::function添加另一层抽象:

class ICustomButton : public IControl {
private:
    std::function<void(void)> action;
public:
    ICustomButton(std::function& f) : action(f)  {
        // some task
    }
    void ICustomButton::OnMouseDown(int x, int y, IMouseMod *pMod) {
        action();
    }
};

现在,创建按钮的呼叫者可以提供std::function<void(void)>的操作,该操作可以嵌入对 pWavePlayer的引用。

这是一个完整的示例,说明了方法:

class Button {
    function<void(void)> action;
public:
    Button(function<void(void)> f) : action(f) {}
    void click() {
        action();
    }
};
class WavePlayer {
public:
    void load() {
        cout << "loaded" << endl;
    }
};
int main() {
    WavePlayer player;
    Button b([&] {
        player.load();
    });
    b.click();
    return 0;
}

演示。

这是观察者模式的典型问题。

在C 中,您可以使用模板轻松实现此功能。您可以将我写的一个用于OSS项目。您可以从此观察者提取它。H

基本上您要做的是,您将鼠标处理程序对象声明为Dispatcher,而任何想要将其作为Listener接收的对象。在鼠标处理程序中,您调用通知方法,然后将通知所有观察者(或听众)。这种方法的优点是,两个对象之间没有依赖性,因此您可以轻松地添加不同的对象,这些对象也可能对此事件感兴趣而不更改对象。

这是一个简单的演示(使用VS2015编译,但也应该在GCC上工作,因为这是我最初开发的地方)。

#include <iostream>
#include <vector>
template <typename... T> class Dispatcher;
template <typename... T> class Listener;
#define ListenerList        std::vector
#define UNUSED(x) (x)
template <typename... T>
class Listener
{
public:
    Listener(void)
    {
    }
    virtual ~Listener(void)
    {
    }
    virtual void handleNotification(Dispatcher<T...> *oSource, T... /* oEvent */)
    {
        // Default implementation does nothing
        // which can be used as a null listener where
        // a listener is expected but doesn't have
        // a meaningful implementation.
        UNUSED(oSource);
    }
    /**
    * The invalidateDispatcher() call is sent when the dispatcher
    * should no longer be accessed anymore. After this call, the listener
    * will no longer receive any notifications and the dispatcher is
    * destroyed, so the listener should not unregister with
    * removeListener().
    */
    virtual void invalidateDispatcher(Dispatcher<T...> const *oDispatcher)
    {
        UNUSED(oDispatcher);
    }
};
template <typename... T>
class Dispatcher
{
public:
    Dispatcher(void)
    {
        mAllowDuplicates = false;
    }
    virtual ~Dispatcher(void)
    {
        invalidate();
    }
    void allowDuplicates(bool bAllowDuplicates = true)
    {
        mAllowDuplicates = bAllowDuplicates;
    }
    /**
    * After the invalidate() message is sent to the listeners,
    * they will no longer receive any notifications and they should
    * no longer access the dispatcher pointer as the object became invalid.
    * When this call is sent, the listener also shouldn't
    * unregister via removeListener().
    */
    virtual void invalidate(void)
    {
        for (Listener<T...> * &listener : mListeners)
            listener->invalidateDispatcher(this);
    }
    virtual void notify(T... oEvent)
    {
        for (Listener<T...> * &listener : mListeners)
            listener->handleNotification(this, oEvent...);
    }
    /**
    * Adds a listener to the dispatcher. A listener
    * can attach itself multiple times, in which case
    * it will receive as many notifications as it
    * is registered. When the listener is removed
    * it will remove all instances with a single call
    * so there is no need to balance the addListener()
    * with removeListener() calls.
    */
    virtual void addListener(Listener<T...> *oListener)
    {
        if (!mAllowDuplicates)
        {
            if (listenerIndex(oListener) != -1)
                return;
        }
        mListeners.push_back(oListener);
    }
    virtual void removeListener(Listener<T...> *oListener)
    {
        // The listener may have registered multiple times
        // so we must remove all instances.
        int i;
        while ((i = listenerIndex(oListener)) != -1)
            mListeners.erase(mListeners.begin() + i);
    }
protected:
    ListenerList<Listener<T...> *> &getListeners(void) const
    {
        return mListeners;
    }
    virtual int listenerIndex(Listener<T...> const *oListener) const
    {
        int i = -1;
        for (Listener<T...> * const &listener : mListeners)
        {
            i++;
            if (listener == oListener)
                return i;
        }
        return -1;
    }
private:
    ListenerList<Listener<T...> *> mListeners;
    bool mAllowDuplicates;
};

class Mousehandler : public Dispatcher<bool /* ButtonState */, int /* x Position */, int /* y Position */>
{
public:
    Mousehandler(void) {}
    void buttonePressed(int nButtonState, int x, int y)
    {
        if (nButtonState == 1)      // Button up
            notify(true, x, y);
        else
            notify(false, x, y);        // Button down.
    }
};

class MouseListener : public Listener<bool, int, int>
{
public:
    MouseListener(int id) { mId = id;  }
    void handleNotification(Dispatcher<bool, int, int> *oSource, bool bButtonPress, int nX, int nY) override
    {
        UNUSED(oSource);
        if (bButtonPress)
            std::cout << mId << ": Button was pressed at " << nX << "/" << nY << std::endl;
        else
            std::cout << mId << ": Button was released at " << nX << "/" << nY << std::endl;
    }
private:
    int mId;
};

int main(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);
    Mousehandler h;
    MouseListener l1(1);
    MouseListener l2(2);
    h.addListener(&l1);
    h.addListener(&l2);
    h.buttonePressed(true, 10, 15);
    h.buttonePressed(false, 20, 11);
    return 0;
}

如果您使用的是较旧的编译器,则可能没有variadic参数,在这种情况下,您必须更改模板才能接受一个键入,并且必须使用指针到结构或类别的指针,如果您需要发送作为您的活动,多个参数以上。使用C 11,它更容易,更清晰。

这是相同的,但使用处理程序上的mulitple事件。

class Mousehandler
    : public Dispatcher<bool /* ButtonState */, int /* x Position */, int /* y Position */>
    , public Dispatcher<int /* x Position */, int /* y Position */>
{
public:
    typedef Dispatcher<bool, int , int > button_handler;
    typedef Dispatcher<int, int > move_handler;
    typedef Listener<bool, int, int > button_listener;
    typedef Listener<int, int > move_listener;
public:
    Mousehandler(void) {}
    void buttonPressed(int nButtonState, int x, int y)
    {
        if (nButtonState == 1)      // Button up
            Dispatcher<bool, int, int>::notify(true, x, y);
        else
            Dispatcher<bool, int, int>::notify(false, x, y);        // Button down.
    }
    void mouseMoved(int x, int y)
    {
        Dispatcher<int, int >::notify(x, y);
    }
    void addButtonListener(button_listener *pListener)
    {
        button_handler::addListener(pListener);
    }
    void addMoveListener(move_listener *pListener)
    {
        move_handler::addListener(pListener);
    }
};

class MouseListener 
    : public Listener<bool, int, int>
    , public Listener<int, int>
{
public:
    MouseListener(int id) { mId = id;  }
    void handleNotification(Mousehandler::button_handler *oSource, bool bButtonPress, int nX, int nY) override
    {
        UNUSED(oSource);
        if (bButtonPress)
            std::cout << mId << ": Button was pressed at " << nX << "/" << nY << std::endl;
        else
            std::cout << mId << ": Button was released at " << nX << "/" << nY << std::endl;
    }
    void handleNotification(Mousehandler::move_handler *oSource, int nX, int nY) override
    {
        UNUSED(oSource);
        std::cout << mId << ": Mouse moved to " << nX << "/" << nY << std::endl;
    }
private:
    int mId;
};

int main(int argc, char *argv[])
{
    UNUSED(argc);
    UNUSED(argv);
    Mousehandler h;
    MouseListener l1(1);
    MouseListener l2(2);
    h.addButtonListener(&l1);
    h.addMoveListener(&l1);
    // No need for movements on the second listener.
    h.addButtonListener(&l2);
    h.buttonPressed(true, 10, 15);
    h.buttonPressed(false, 20, 11);
    h.mouseMoved(12, 20);
    h.mouseMoved(21, 23);
    return 0;
}