让我这样的课程,例如:
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;
}