您的位置:首页 > 其它

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

2014-09-26 14:40 176 查看
在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;
};
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: