我正在尝试在DirectX9中编写回调事件系统。我正在尝试使用方法函数指针来触发鼠标点击事件;但是我遇到了一些问题。我的游戏使用游戏状态管理器来管理渲染。我的所有游戏状态都来自基类AbstractGameState。
我有一个使用这种特定方法的精灵对象:
m_NewGameSprite->OnClick(this, &MainMenuState::StartGame);
MainMenuState是我游戏所在的当前游戏状态,而StartGame是此类的void方法部分。我想将函数指针存储在我的精灵类中的变量中,这样我就可以在用户点击时执行它。
template <typename T>
void OnClick(GameState* LinkedState, void (T::*EventPointer)())
{
m_LinkedGameState = LinkedState;
m_EventPointer = EventPointer; // <- Doesnt compile
}
我尝试了向下转换指针,但这确实不起作用。
我的精灵类也包含这两个变量
void (GameState::*m_EventPointer)();
GameState* m_LinkedGameState;
任何帮助将不胜感激
答案 0 :(得分:2)
我真的不知道为什么你的任务不起作用,毫无疑问,litb将很快解释原因。 Boost.Function是一个漂亮的,通用的,类型安全的标准函数对象,可以在几乎所有情况下用作函数指针的替代。我会这样做:
typedef boost::function0<void> Event;
Event m_Event;
请注意,事件类现在封装了函数调用的状态,包括要调用它的对象。通常你也使用Boost.Bind来创建一个闭包,但你也可以很容易地绑定到一个自由函数或一些其他函数对象。
void OnClick(Event &event)
{
m_Event=event;
}
这样称呼:
OnClick(boost::bind(&MainMenuState::StartGame, this));
使用此方案,您实际上不需要存储“链接游戏状态” - 这是封装在事件对象中。
答案 1 :(得分:1)
此处的问题是StartGame
无法使用GameState
参数调用任何this
实例。只能使用类型为this
的{{1}}参数调用它。
要MainMenuState
指向void (GameState::*)()
中定义的方法,该方法必须是MainMenuState
中定义的虚方法。
我建议不要试图存储成员函数指针,而是使用以下内容存储“命令对象”(或函子)指针:
GameState
然后定义这样的实现:
class Callback
{
public:
virtual void Execute() = 0;
};
然后,您可以定义template <typename TClass>
class MemberCallback : public Callback
{
public:
MemberCallBack(TClass * pThis, void (TClass::*pFunc)() )
{
m_pThis = pThis;
m_pFunc = pFunc;
}
void Execute()
{
m_pThis->*m_pFunc();
}
private:
TClass * m_pThis;
void (TClass::*m_pFunc)();
};
类型的成员。
答案 2 :(得分:1)
我有一个非常类似的问题。如果我正确地读到这个,你试图在Java中编写一个类似于ActionEvent
的系统。这看起来像这样:
Component.addActionListener( new ActionEventListener( new ActionEvent(
public void execute() { /* Component clicked Do Something */ } )));
在C ++中,有一个非常类似的事情要做,如下所示。
在我看来,这要好得多,因为你可以实际重用方法调用并随意修改它们,这会给你更大的灵活性。
的 Event.hpp 强> 的
#pragma once
class Event
{
public:
Event(void) { }
~Event(void) { }
public:
//Stores a function to callback. Example shown in Main.cpp
void operator += (void (*callback)())
{
this->callback = callback;
}
//Executes our stored call back method at any given time.
void execute(void)
{
callback();
}
//Variable of type VOID* -> This is a function which is stored + executed.
private:
void (*callback)();
};
的 Main.cpp的强> 的
#include <iostream>
#include "Event.hpp"
using namespace std;
void print(void)
{
cout << "Hello This Works" << endl;
}
void print2(void)
{
cout << "Success again!" << endl;
}
int main()
{
Event task_event;
task_event += print;
task_event.execute();
task_event += print2;
task_event.execute();
system("pause");
return 0;
}
答案 3 :(得分:0)
为什么你在那里使用模板功能?对于GameState以外的OnClick
,T
将无法编译或工作。
但是,在这种情况下,我通常不会使用指向成员函数的指针。我创建了一个函数 object ,重载operator()
,然后传递它们。这样更好:语法更清晰,如果你发现需要,你可以在函数对象中存储一些状态。
答案 4 :(得分:0)
您应该将参数作为:
void (GameState::*EventPointer)()
因为一旦它是一个指针,就不能像那样向下转发(它没有关于基类中是否存在相同指针的信息)。这些函数必须在GameState上才能工作(可能是虚拟的)
然而!由于您正在进行回调(基本上是观察者模式),因此您可能需要仔细查看boost::signals。它将完成所有这些(以及更多)。您无需为GameState添加功能。
class Foo {
// ...
template <typename T>
boost::signals::connection OnClick(const boost::function<void ()>& func)
{
return sig.connect(func);
}
void FireClick() { sig_(); }
private:
boost::signal<void ()> sig_;
};
int this_will_be_called() { }
Foo f = // ...;
f.OnClick(boost::bind(&this_will_be_called));