C++委托事件.docx
《C++委托事件.docx》由会员分享,可在线阅读,更多相关《C++委托事件.docx(50页珍藏版)》请在冰豆网上搜索。
C++委托事件
[置顶]在C++中模拟委托事件的方法
(一)
分类:
C++2011-11-2113:
431410人阅读评论(4)收藏举报
关键字:
C++、事件、指向函数的指针、静态方法、接口、多重继承、模板
如果对C#、Delphi等语言熟悉,都知道这些语言支持委托事件的方法使用起来非常方便,并且能够较好的降低类的依赖关系,而C++语言没有集成这种语法,所以要想在C++使用这种事件模式需要开发人员自己来模拟,本文将提供多种模拟的方案,并会分析各自的优缺点,可以在需要的时候有选择的使用。
委托事件模式的具体是什么样子的,可以举个例子,类A的实例a使用类B的实例b,当a工作时调用b的一个方法bf来操作,比如b的这个方法bf是个长时间操作,方法bf每工作一段就通过事件的方式通知a进度情况,类A的一个事件接收方法ae接收到事件后就会执行一些比如界面更新的工作,这里本质上来看就是bf方法会调用ae方法,对于a对象看起来就是被事件通知了。
值得注意的是这里的委托事件模式与Windows的消息循环体系是不同的,通常Windows的消息是放到消息队列中,应用程序进程从队列中得到消息,然后调用消息处理过程来处理消息,这里是真正的消息通知,并且消息处理过程是有固定的函数声明的,不能更改成其他的格式,但是委托事件模式实际上就是一次函数调用,整个事件触发与调用处理结束通常都是在一个Windows消息处理过程中完成的,并不会发送真正的Windows消息,并且事件接收处理函数可以是任意的类B预定义函数声明格式函数,种类可以非常多。
委托事件模式的使用,其好处是在开发中可以像真正的消息事件体系一样来理解整个体系模式,可以做到很好的接口分离,最大程度上分离开了类B对于类A的依赖,类B在开发时完全可以不需要知道类A的存在,同时类A在开发时可以有选择的选取接收类B的事件进行处理。
对于C++的开发人员看到这里很快就可以联想到指向函数的指针,b持有a的函数指针就可以调用了,很类似Windows中的CALLBACK函数,确实指向函数的指针是模拟实现的一种方法或者说是基础,但是直接使用C语法中指向函数的指针的方法在处理C++类时还是会碰到很多问题,C++类的成员函数的定义是跟具体的类直接相关的(每个函数都有一个指向这个类this指针的参数),所以函数的声明不固定,所以目前的通常做法都是定义类静态成员函数的方法来实现,但是静态函数成员在处理不同的对象实例的调用时如果没有对象实例的指针就不能直接访问类的实例成员,所以这里会有很多问题,最明显的就是对于类A只能使用静态方法来作为回调的函数,当有A的多个实例时就会有些麻烦,为了排除这些麻烦或者说规范这些问题,本文后续章节会提供多种方式来模拟委托事件方式。
本文谈到的所有例子代码我都已经上传,具体下载地址是studio2005平台调试通过,对于本文及例子中提到的一些主要类如下:
事件触发对象类叫做CNotifyClass,事件接收对象类或事件处理对象类叫做CRecvEventClass、CRecvEventClassOne等等。
例子代码被修正过,新的下载地址被更新了,并且是免资源分下载的了
在C++中模拟委托事件的方法
(二)
分类:
C++2011-11-2210:
24469人阅读评论
(1)收藏举报
下边来看具体的实现方案
一、静态函数模拟事件
对应的例子工程名StaticFunctionEvent
1、 具体的实现方法
(1)、事件触发对象类CNotifyClass的类定义如下:
viewplaincopytoclipboardprint?
1.typedef void (*PEVENT_NOPARAM_NORETURN)(void *);
2.typedef int (*PEVENT_NOPARAM_RETURN)(void *);
3.typedef void (*PEVENT_PARAM_NORETURN)(void *, int);
4.typedef int (*PEVENT_PARAM_RETURN)(void *, int);
5.
6.class CNotifyClass
7.{
8.public:
9. CNotifyClass(void);
10. ~CNotifyClass(void);
11.
12.public:
13. bool RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
14. PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter);
15. void UnRegisterEvent();
16.
17. void DoNotifyEventWork();
18.
19.protected:
20. PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
21. PEVENT_NOPARAM_RETURN m_pNoParam_Return_EventHandler;
22. PEVENT_PARAM_NORETURN m_pParam_NoReturn_EventHandler;
23. PEVENT_PARAM_RETURN m_pParam_Return_EventHandler;
24.
25.protected:
26. void *m_pEventParameter;
27.};
类实现如下:
viewplaincopytoclipboardprint?
1.#include "NotifyClass.h"
2.
3.CNotifyClass:
:
CNotifyClass(void)
4. :
m_pEventParameter(NULL)
5. , m_pNoParam_NoReturn_EventHandler(NULL)
6. , m_pNoParam_Return_EventHandler(NULL)
7. , m_pParam_NoReturn_EventHandler(NULL)
8. , m_pParam_Return_EventHandler(NULL)
9.{
10.}
11.
12.CNotifyClass:
:
~CNotifyClass(void)
13.{
14. UnRegisterEvent();
15.}
16.
17.bool
18.CNotifyClass:
:
RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
19. PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter)
20.{
21. if (NULL !
= m_pNoParam_NoReturn_EventHandler)
22. return false;
23. m_pEventParameter = pParameter;
24. m_pNoParam_NoReturn_EventHandler = pFunc1;
25. m_pNoParam_Return_EventHandler = pFunc2;
26. m_pParam_NoReturn_EventHandler = pFunc3;
27. m_pParam_Return_EventHandler = pFunc4;
28. return true;
29.}
30.
31.void
32.CNotifyClass:
:
UnRegisterEvent()
33.{
34. m_pEventParameter = NULL;
35. m_pNoParam_NoReturn_EventHandler = NULL;
36. m_pNoParam_Return_EventHandler = NULL;
37. m_pParam_NoReturn_EventHandler = NULL;
38. m_pParam_Return_EventHandler = NULL;
39.}
40.
41.void
42.CNotifyClass:
:
DoNotifyEventWork()
43.{
44. int iResult = 0;
45. if (m_pNoParam_NoReturn_EventHandler !
= NULL)
46. m_pNoParam_NoReturn_EventHandler(m_pEventParameter);
47. if (m_pNoParam_Return_EventHandler !
= NULL)
48. iResult = m_pNoParam_Return_EventHandler(m_pEventParameter);
49.
50. iResult = iResult + 10;
51. if (m_pParam_Return_EventHandler !
= NULL)
52. iResult = m_pParam_Return_EventHandler(m_pEventParameter, iResult);
53. iResult = iResult + 10;
54. if (m_pParam_NoReturn_EventHandler !
= NULL)
55. m_pParam_NoReturn_EventHandler(m_pEventParameter, iResult);
56.}
类CNotifyClass声明时要提供类似如下的事件处理函数声明,所有接收事件的对象都要使用如下的函数声明来定义函数
viewplaincopytoclipboardprint?
1.typedef void (*PEVENT_NOPARAM_NORETURN)(void*);
事件接收对象类通过调用RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,当会触发消息的工作方法DoNotifyEventWork被调用时,会检测哪些事件被注册了,被注册了就调用,就如同进行了事件通知。
(2)、事件接收对象类或事件处理对象类CRecvEventClass的类定义如下:
viewplaincopytoclipboardprint?
1.class CRecvEventClass
2.{
3.public:
4. CRecvEventClass(void);
5. ~CRecvEventClass(void);
6.
7.public:
8. int DoWork(int iArg);
9.
10.protected:
11. static void OnNoParamNoReturnEvent(void *pvParam);
12. static int OnNoParamReturnEvent(void *pvParam);
13. static void OnParamNoReturnEvent(void *pvParam, int iArg);
14. static int OnParamReturnEvent(void *pvParam, int iArg);
15.
16.protected:
17. CNotifyClass m_ncNotify;
18. int m_nNum;
19.};
类实现如下:
viewplaincopytoclipboardprint?
1.#include "RecvEventClass.h"
2.
3.CRecvEventClass:
:
CRecvEventClass(void)
4.{
5. m_ncNotify.RegisterEvent(OnNoParamNoReturnEvent, OnNoParamReturnEvent, OnParamNoReturnEvent, OnParamReturnEvent, this);
6.}
7.
8.CRecvEventClass:
:
~CRecvEventClass(void)
9.{
10. m_ncNotify.UnRegisterEvent();
11.}
12.
13.int
14.CRecvEventClass:
:
DoWork(int iArg)
15.{
16. m_nNum = iArg;
17. m_ncNotify.DoNotifyEventWork();
18. return m_nNum;
19.}
20.
21.void
22.CRecvEventClass:
:
OnNoParamNoReturnEvent(void *pvParam)
23.{
24. _tprintf(_T("Run OnNoParamNoReturnEvent\n"));
25. if (pvParam !
= NULL)
26. {
27. CRecvEventClass *p = reinterpret_cast(pvParam);
28. p->m_nNum = p->m_nNum + 10;
29. }
30.}
31.
32.int
33.CRecvEventClass:
:
OnNoParamReturnEvent(void *pvParam)
34.{
35. _tprintf(_T("Run OnNoParamReturnEvent\n"));
36. if (pvParam !
= NULL)
37. {
38. CRecvEventClass *p = reinterpret_cast(pvParam);
39. p->m_nNum = p->m_nNum + 10;
40. return p->m_nNum;
41. }
42. else
43. return 0;
44.}
45.
46.void
47.CRecvEventClass:
:
OnParamNoReturnEvent(void *pvParam, int iArg)
48.{
49. _tprintf(_T("Run OnParamNoReturnEvent\n"));
50. if (pvParam !
= NULL)
51. {
52. CRecvEventClass *p = reinterpret_cast(pvParam);
53. p->m_nNum = iArg + 10;
54. }
55.}
56.
57.int
58.CRecvEventClass:
:
OnParamReturnEvent(void *pvParam, int iArg)
59.{
60. _tprintf(_T("Run OnParamReturnEvent\n"));
61. int iRet = iArg + 10;
62. return iRet;
63.}
这里事件接收对象类或事件处理对象类CRecvEventClass在定义时一定要定义同事件触发类CNotifyClass提供的方法声明相同的方法来接收处理事件,由于CNotifyClass定义的方法声明是普通的指向函数指针,不包含对象的this指针传递,所以这种方法不能是实例成员函数,只能是静态成员方法,就如同
viewplaincopytoclipboardprint?
1.static void OnNoParamNoReturnEvent(void *pvParam);
当定义了事件处理方法后,可以通过调用类CNotifyClass的方法RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,本例是在构造和析构函数中调用的,当CNotifyClass类的DoNotifyEventWork方法被调用时,就会通过函数指针事件通知到类CRecvEventClass,在事件处理方法中为了能区分CRecvEventClass的不同实例,所以要在类CNotifyClass的方法RegisterEvent中提供了pParameter参数,通常CRecvEventClass在调用RegisterEvent时传递对象本身的this指针,这样在事件处理方法中就可以通过pvParam参数得到这个指针的类实例的this指针,就像OnNoParamNoReturnEvent中的代码
viewplaincopytoclipboardprint?
1.CRecvEventClass:
:
OnNoParamNoReturnEvent(void *pvParam)
2.{
3. _tprintf(_T("Run OnNoParamNoReturnEvent\n"));
4. if (pvParam !
= NULL)
5. {
6. CRecvEventClass *p = reinterpret_cast(pvParam);
7. p->m_nNum = p->m_nNum + 10;
8. }
9.}
可以直接将pvParam参数转化为CRecvEventClass类实例的指针,然后就像成员方法一样访问类实例的其他方法和成员了。
(3)、使用的例子及输出
viewplaincopytoclipboardprint?
1.int _tmain(int argc, _TCHAR* argv[])
2.{
3. //2个对象,虽然类里用的静态方法,但是不会影响到不同对象的调用,互相不会干扰其他的对象的状态
4. CRecvEventClass rec1, rec2;
5. int iIn, iOut;
6.
7. iIn = 10;
8. iOut = rec1.DoWork(iIn);
9. _tprintf(_T("StaticFunctionEvent test 1, Init:
%d, Result:
%d\n"), iIn, iOut);
10. iIn = 30;
11. iOut = rec1.DoWork(iIn);
12. _tprintf(_T("StaticFunctionEvent test 2, Init:
%d, Result:
%d\n"), iIn, iOut);
13. iIn = 60;
14. iOut = rec2.DoWork(iIn);
15. _tprintf(_T("StaticFunctionEvent test 3, Init:
%d, Result:
%d\n"), iIn, iOut);
16.
17. TCHAR c;
18. _tscanf(_T("%c"), &c);
19. return 0;
20.}
输出结果为:
viewplaincopytoclipboardprint?
1.Run OnNoParamNoReturnEvent
2.Run OnNoParamReturnEvent
3.Run OnParamReturnEvent
4.Run OnParamNoReturnEvent
5.StaticFunctionEvent test 1, Init:
10, Result:
70
6.Run OnNoParamNoReturnEvent
7.Run OnNoParamReturnEvent
8.Run OnParamReturnEvent
9.Run OnParamNoReturnEvent
10.StaticFunctionEvent test 2, Init:
30, Result:
90
11.Run OnNoParamNoReturnEvent
12.Run OnNoParamReturnEvent
13.Run OnParamReturnEvent
14.Run OnParamNoReturnEvent
15.StaticFunctionEvent test 3, Init:
60, Result:
120
从输出结果上看事件是被顺次调用了,至于程序中的变量值,主要表明这些事件执行的而动作是对应于实例的,不是因为是静态方法就是对应于类的。
2、 实现的要点
(1)、事件触发类的实现要点
a、 事件触发类必须要定义要处理事件的函数声明
b、 事件触发类要定义相应函数声明类型的成员变量
viewplaincopytoclipboardprint?
1.PEVENT_NO