类成员函数作为回调函数/事件模型

在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;
};



 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值