我想说我的课程如下:
class IWavePlayer : public IControl
{
private:
WDL_String mWavePath;
public:
IWavePlayer() {
// some task
}
void LoadWave() {
mWavePath = PromptForFile();
// some task with mWavePath
}
};
我使用IWavePlayer pWavePlayer;
在主要实例中添加了它。
现在,我需要调用(在另一个控件中"处理"鼠标单击)LoadWave()
的函数pWavePlayer
:
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个按钮)。
您通常如何处理这种情况?这样做的正确模式是什么?
答案 0 :(得分: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();
}
};
这也可以通过提供某种"定位服务来实现。或者#34;注册表"通过它可以找到指向IWavePlayer
的指针而不提供任何引用。例如,如果系统中只有一个IWavePlayer
,则可以将其设为单个,这意味着它可以作为IWavePlayer::instance
在整个系统中访问。
如果您不想在IWavePlayer
中引用ICustomButton
,可以使用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;
}
答案 1 :(得分:1)
这是Observer Pattern的典型问题。
在C ++中,您可以使用模板轻松实现此功能。您可以使用我为OSS项目编写的一个。您可以从此observer.h
中提取它基本上,您将鼠标处理程序对象声明为Dispatcher
,并将任何对象想要将其作为Listener
接收。在鼠标处理程序中,调用notify方法,然后将通过您的事件通知所有观察者(或监听器)。这种方法的优点是,两个对象之间没有依赖关系,因此您可以轻松添加可能对此事件感兴趣的不同对象,而无需更改对象。
这是一个简单的演示(使用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;
}
如果您使用的是较旧的编译器,则可能没有可变参数,在这种情况下,您必须更改模板以仅接受一个类型名称,并且如果需要发送,则必须使用指向结构或类的指针您的活动不止一个参数。使用C ++ 11,它更容易,IMO更清晰。
这是相同的,但在处理程序上使用了多个事件。
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;
}