在C++中,有需要将类的成员函数作为某一个组件/API的回调函数的需求,应对这种需求,可以使用:
1、C++ 11里面的 std::function 和 std::bind 代替回调函数;
class EventArgs
{
public:
EventArgs(){}
virtual ~EventArgs(){}
};
typedef std::function<void(void* sender, EventArgs* args)> EventHandler;
class Wnd
{
public:
Input::EventHandler Event_Init;
void Dosth()
{
if (Event_Init)
{
EventArgs args;
Event_Init(this, &args);
}
}
};
class APP
{
public:
APP()
{
m_wnd.Event_Init = EVENT_SLOT(&APP::OnEvent_Init);
}
~APP(){}
void OnEvent_Init(void* _sender, EventArgs* _args)
{
}
Wnd m_wnd;
};
如果是调用系统API之类的接口,就不能用这种方式了;
2、使用命令模式的变种:
class Event
{
public:
Event(){}
virtual ~Event(){}
virtual void Invoke(void* _sender, EventArgs* _args) = 0;
};
template <class Receiver>
class EventSTD : public Event
{
public:
//事件处理函数签名
typedef void (Receiver:: * Action)(void* _sender, EventArgs* _args);
//构造器
EventSTD(Receiver* r, Action a) : _receiver(r), _action(a){}
virtual void Invoke(void* _sender, EventArgs* _args)
{
(_receiver->*_action)(_sender, _args);
}
private:
Action _action;
Receiver* _receiver;
};
应用:
class EventArgs_Btn : public EventArgs
{
public:
EventArgs_Btn(){}
~EventArgs_Btn(){}
public:
int x;
int y;
bool IsPress;
};
class ButtonEx
{
public:
//所有事件都可以使用该模型定义(可以使用智能指针,隐藏释放过程)
Event* Event_Init;
ButtonEx() : Event_Init(nullptr){}
~ButtonEx()
{
//release
delete Event_Init;
Event_Init = nullptr;
}
void DoSth()
{
if (Event_Init == nullptr)
return;//error
EventArgs_Btn args;
args.x = 10;
args.y = 20;
args.IsPress = false;
Event_Init->Invoke(this, &args);
if (args.IsPress)
{
//feedback ...
cout << "btn press!" << endl;
}
}
private:
};
class Wnd
{
public:
Wnd()
{
m_Btn.Event_Init = new EventSTD<Wnd>(this, &Wnd::Init);
}
~Wnd()
{
}
void Init(void* _sender, EventArgs* _args)
{
//use and convert sender;
//
auto pArgs = reinterpret_cast<EventArgs_Btn*>(_args);
if (pArgs == nullptr){
//error handler
return;
}
int x = pArgs->x;
int y = pArgs->y;
pArgs->IsPress = true;
}
void Proc()
{
m_Btn.DoSth();
}
private:
ButtonEx m_Btn;
};
int _tmain(int argc, _TCHAR* argv[])
{
Wnd wnd;
wnd.Proc();
return 0;
}
这种方式是借鉴了C#中的事件模型,所有的事件响应函数签名如下:
void OnEvent(void* _sender, EventArgs* _args)
事件的入参和出参都放在事件参数对象EventArgs里面,你可以派生生成不同的参数对象,如例子中的EventArgs_Btn;
这种签名可以实现一对多的订阅,即一个OnEvent函数可以订阅多个事件,然后通过sender来区分不同的触发源。
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
附命令模型代码:
class Command
{
public:
virtual ~Command();
virtual void Execute() = 0;
virtual void UnExecute();
protected:
Command();
};
template <class Receiver>
class SimpleCommand : public Command
{
public:
typedef void (Receiver:: * Action)();
SimpleCommand(Receiver* r, Action a) : _receiver(r), _action(a){}
virtual void Execute(){
(_receiver->*_action)();
}
private:
Action _action;
Receiver* _receiver;
};
class Button
{
public:
void Click()
{
_BtnClick->Execute();
}
public:
Command* _BtnClick;
};
class Client
{
public:
Client()
{
_btn._BtnClick = new SimpleCommand<Client>(this, &Client::OnBtnClick);
}
//Receiver
void OnBtnClick()
{
cout<<"Client::OnBtnClick"<<endl;
}
void OnBtnClick2()
{
}
public:
Button _btn;
};