在C++中模拟委托事件的方法(上篇)
2011-11-26 11:14
357 查看
出处:http://blog.csdn.net/gogogo/article/details/6996290
作者:gogogo
关键字: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的多个实例时就会有些麻烦,为了排除这些麻烦或者说规范这些问题,本文后续章节会提供多种方式来模拟委托事件方式。
本文谈到的所有例子代码我都已经上传,具体下载地址是http://download.csdn.net/detail/gogogo/3830459,所有程序在Visual studio 2005平台调试通过,对于本文及例子中提到的一些主要类如下:事件触发对象类叫做CNotifyClass,事件接收对象类或事件处理对象类叫做CRecvEventClass、CRecvEventClassOne等等。
例子代码被修正过,新的下载地址被更新了,并且是免资源分下载的了
下边来看具体的实现方案
一、静态函数模拟事件
对应的例子工程名StaticFunctionEvent
1、 具体的实现方法
(1)、事件触发对象类CNotifyClass的类定义如下:
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void *);
typedef int (*PEVENT_NOPARAM_RETURN)(void *);
typedef void (*PEVENT_PARAM_NORETURN)(void *, int);
typedef int (*PEVENT_PARAM_RETURN)(void *, int);
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter);
void UnRegisterEvent();
void DoNotifyEventWork();
protected:
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
PEVENT_NOPARAM_RETURN m_pNoParam_Return_EventHandler;
PEVENT_PARAM_NORETURN m_pParam_NoReturn_EventHandler;
PEVENT_PARAM_RETURN m_pParam_Return_EventHandler;
protected:
void *m_pEventParameter;
};
类实现如下:
view
plain
#include "NotifyClass.h"
CNotifyClass::CNotifyClass(void)
: m_pEventParameter(NULL)
, m_pNoParam_NoReturn_EventHandler(NULL)
, m_pNoParam_Return_EventHandler(NULL)
, m_pParam_NoReturn_EventHandler(NULL)
, m_pParam_Return_EventHandler(NULL)
{
}
CNotifyClass::~CNotifyClass(void)
{
UnRegisterEvent();
}
bool
CNotifyClass::RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter)
{
if (NULL != m_pNoParam_NoReturn_EventHandler)
return false;
m_pEventParameter = pParameter;
m_pNoParam_NoReturn_EventHandler = pFunc1;
m_pNoParam_Return_EventHandler = pFunc2;
m_pParam_NoReturn_EventHandler = pFunc3;
m_pParam_Return_EventHandler = pFunc4;
return true;
}
void
CNotifyClass::UnRegisterEvent()
{
m_pEventParameter = NULL;
m_pNoParam_NoReturn_EventHandler = NULL;
m_pNoParam_Return_EventHandler = NULL;
m_pParam_NoReturn_EventHandler = NULL;
m_pParam_Return_EventHandler = NULL;
}
void
CNotifyClass::DoNotifyEventWork()
{
int iResult = 0;
if (m_pNoParam_NoReturn_EventHandler != NULL)
m_pNoParam_NoReturn_EventHandler(m_pEventParameter);
if (m_pNoParam_Return_EventHandler != NULL)
iResult = m_pNoParam_Return_EventHandler(m_pEventParameter);
iResult = iResult + 10;
if (m_pParam_Return_EventHandler != NULL)
iResult = m_pParam_Return_EventHandler(m_pEventParameter, iResult);
iResult = iResult + 10;
if (m_pParam_NoReturn_EventHandler != NULL)
m_pParam_NoReturn_EventHandler(m_pEventParameter, iResult);
}
类CNotifyClass声明时要提供类似如下的事件处理函数声明,所有接收事件的对象都要使用如下的函数声明来定义函数
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void*);
事件接收对象类通过调用RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,当会触发消息的工作方法DoNotifyEventWork被调用时,会检测哪些事件被注册了,被注册了就调用,就如同进行了事件通知。
(2)、事件接收对象类或事件处理对象类CRecvEventClass的类定义如下:
view
plain
class CRecvEventClass
{
public:
CRecvEventClass(void);
~CRecvEventClass(void);
public:
int DoWork(int iArg);
protected:
static void OnNoParamNoReturnEvent(void *pvParam);
static int OnNoParamReturnEvent(void *pvParam);
static void OnParamNoReturnEvent(void *pvParam, int iArg);
static int OnParamReturnEvent(void *pvParam, int iArg);
protected:
CNotifyClass m_ncNotify;
int m_nNum;
};
类实现如下:
view
plain
#include "RecvEventClass.h"
CRecvEventClass::CRecvEventClass(void)
{
m_ncNotify.RegisterEvent(OnNoParamNoReturnEvent, OnNoParamReturnEvent, OnParamNoReturnEvent, OnParamReturnEvent, this);
}
CRecvEventClass::~CRecvEventClass(void)
{
m_ncNotify.UnRegisterEvent();
}
int
CRecvEventClass::DoWork(int iArg)
{
m_nNum = iArg;
m_ncNotify.DoNotifyEventWork();
return m_nNum;
}
void
CRecvEventClass::OnNoParamNoReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
}
}
int
CRecvEventClass::OnNoParamReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
return p->m_nNum;
}
else
return 0;
}
void
CRecvEventClass::OnParamNoReturnEvent(void *pvParam, int iArg)
{
_tprintf(_T("Run OnParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = iArg + 10;
}
}
int
CRecvEventClass::OnParamReturnEvent(void *pvParam, int iArg)
{
_tprintf(_T("Run OnParamReturnEvent\n"));
int iRet = iArg + 10;
return iRet;
}
这里事件接收对象类或事件处理对象类CRecvEventClass在定义时一定要定义同事件触发类CNotifyClass提供的方法声明相同的方法来接收处理事件,由于CNotifyClass定义的方法声明是普通的指向函数指针,不包含对象的this指针传递,所以这种方法不能是实例成员函数,只能是静态成员方法,就如同
view
plain
static void OnNoParamNoReturnEvent(void *pvParam);
当定义了事件处理方法后,可以通过调用类CNotifyClass的方法RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,本例是在构造和析构函数中调用的,当CNotifyClass类的DoNotifyEventWork方法被调用时,就会通过函数指针事件通知到类CRecvEventClass,在事件处理方法中为了能区分CRecvEventClass的不同实例,所以要在类CNotifyClass的方法RegisterEvent中提供了pParameter参数,通常CRecvEventClass在调用RegisterEvent时传递对象本身的this指针,这样在事件处理方法中就可以通过pvParam参数得到这个指针的类实例的this指针,就像OnNoParamNoReturnEvent中的代码
view
plain
CRecvEventClass::OnNoParamNoReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
}
}
可以直接将pvParam参数转化为CRecvEventClass类实例的指针,然后就像成员方法一样访问类实例的其他方法和成员了。
(3)、使用的例子及输出
view
plain
int _tmain(int argc, _TCHAR* argv[])
{
//2个对象,虽然类里用的静态方法,但是不会影响到不同对象的调用,互相不会干扰其他的对象的状态
CRecvEventClass rec1, rec2;
int iIn, iOut;
iIn = 10;
iOut = rec1.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 1, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 30;
iOut = rec1.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 2, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 60;
iOut = rec2.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 3, Init:%d, Result:%d\n"), iIn, iOut);
TCHAR c;
_tscanf(_T("%c"), &c);
return 0;
}
输出结果为:
view
plain
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 1, Init:10, Result:70
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 2, Init:30, Result:90
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 3, Init:60, Result:120
从输出结果上看事件是被顺次调用了,至于程序中的变量值,主要表明这些事件执行的而动作是对应于实例的,不是因为是静态方法就是对应于类的。
2、 实现的要点
(1)、事件触发类的实现要点
a、 事件触发类必须要定义要处理事件的函数声明
b、 事件触发类要定义相应函数声明类型的成员变量
view
plain
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
c、 为了能传递事件接收类的实例,要定义事件接收类的实例指针
view
plain
void *m_pEventParameter;
d、 或者把上面的函数指针成员和事件接收类的实例指针直接公开被外部修改,或者使用类似RegisterEvent和UnRegisterEvent的方法提供对这些成员的修改,外部事件接收类才能接收事件
e、 在工作时,需要触发事件的地方,通过调用函数指针成员来事件通知事件接收类
(2)、事件接收对象类或事件处理对象类的实现要点
a、 使用静态方法定义与实现满足事件接收函数声明的方法
b、 使用这些静态方法及自身作为参数调用事件触发类的RegisterEvent和UnRegisterEvent的方法,或者直接修改事件触发类的相应成员(如果提供了访问权限)
c、 在这些静态方法中,转换参数为this,然后进行工作,当有事件触发时就可以得到通知被调用。
3、 优缺点
(1)、优点
a、可以部分接收事件,如果不接收什么事件,只需要在调用RegisterEvent时传递NULL给对应的事件函数就可以
b、事件处理方法不需要必须是public的方法,任意访问类别都可以
(2)、缺点
类型安全度相对较底,需要类型转换,不容易在编译期间发现问题,这里主要是指传递的参数pParameter,对于事件处理函数的指针是类型安全的
二、接口模拟事件
对应的例子工程名InterfaceEvent
由于C++不支持接口,但是支持抽象类和多重继承,所以可以通过class或struct模拟实现接口。
1、 具体的实现方法
(1)、事件触发对象类CNotifyClass的类定义如下:
view
plain
class INotifyClassEvent
{
public:
INotifyClassEvent() {};
virtual ~INotifyClassEvent() {};
public:
virtual void OnNoParamNoReturnEvent() = 0;
virtual int OnNoParamReturnEvent() = 0;
virtual void OnParamNoReturnEvent(int) = 0;
virtual int OnParamReturnEvent(int) = 0;
};
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(INotifyClassEvent *pncRecvEvent);
void UnRegisterEvent();
void DoNotifyEventWork();
protected:
INotifyClassEvent *m_pncRecvEvent;
};
类实现如下:
view
plain
#include "NotifyClass.h"
CNotifyClass::CNotifyClass(void)
: m_pncRecvEvent(NULL)
{
}
CNotifyClass::~CNotifyClass(void)
{
UnRegisterEvent();
}
bool
CNotifyClass::RegisterEvent(INotifyClassEvent *pncRecvEvent)
{
if (NULL != m_pncRecvEvent)
return false;
m_pncRecvEvent = pncRecvEvent;
return true;
}
void
CNotifyClass::UnRegisterEvent()
{
m_pncRecvEvent = NULL;
}
void
CNotifyClass::DoNotifyEventWork()
{
if (NULL == m_pncRecvEvent)
return;
int iResult = 0;
m_pncRecvEvent->OnNoParamNoReturnEvent();
iResult = m_pncRecvEvent->OnNoParamReturnEvent();
iResult = iResult + 10;
iResult = m_pncRecvEvent->OnParamReturnEvent(iResult);
iResult = iResult + 10;
m_pncRecvEvent->OnParamNoReturnEvent(iResult);
}
类CNotifyClass声明时要定义事件处理函数集的接口INotifyClassEvent,所有接收事件的类都要继承并实现这个接口来接收事件。
事件接收对象类通过调用RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,当会触发事件的工作方法DoNotifyEventWork被调用时,会检测哪些事件被注册了,被注册了就调用,就如同进行了事件通知。
(2)、事件接收对象类或事件处理对象类CRecvEventClass的类定义如下:
view
plain
class CRecvEventClass : public INotifyClassEvent
{
public:
CRecvEventClass(void);
~CRecvEventClass(void);
public:
int DoWork(int iArg);
protected:
virtual void OnNoParamNoReturnEvent();
virtual int OnNoParamReturnEvent();
virtual void OnParamNoReturnEvent(int iArg);
virtual int OnParamReturnEvent(int iArg);
protected:
CNotifyClass m_ncNotify;
int m_nNum;
};
类实现如下:
view
plain
#include "RecvEventClass.h"
CRecvEventClass::CRecvEventClass(void)
{
m_ncNotify.RegisterEvent(this);
}
CRecvEventClass::~CRecvEventClass(void)
{
m_ncNotify.UnRegisterEvent();
}
int
CRecvEventClass::DoWork(int iArg)
{
m_nNum = iArg;
m_ncNotify.DoNotifyEventWork();
return m_nNum;
}
void
CRecvEventClass::OnNoParamNoReturnEvent()
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
m_nNum = m_nNum + 10;
}
int
CRecvEventClass::OnNoParamReturnEvent()
{
_tprintf(_T("Run OnNoParamReturnEvent\n"));
m_nNum = m_nNum + 10;
return m_nNum;
}
void
CRecvEventClass::OnParamNoReturnEvent(int iArg)
{
_tprintf(_T("Run OnParamNoReturnEvent\n"));
m_nNum = iArg + 10;
}
int
CRecvEventClass::OnParamReturnEvent(int iArg)
{
int iRet;
_tprintf(_T("Run OnParamReturnEvent\n"));
iRet = iArg + 10;
return iRet;
}
这里事件接收对象类或事件处理对象类CRecvEventClass在定义时一定要继承事件通知接口INotifyClassEvent,通过实现这个接口的成员函数来接收事件通知。
当定义了事件处理方法后,可以通过调用类CNotifyClass的方法RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,本例是在构造和析构函数中调用的,当CNotifyClass类的DoNotifyEventWork方法被调用时,就会通过接口方法直接事件通知(调用)相应的事件处理方法
view
plain
void
CRecvEventClass::OnNoParamNoReturnEvent()
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
m_nNum = m_nNum + 10;
}
这里可以看到事件处理方法直接就是类成员方法,不需要传递对象的指针this。
(3)、使用的例子及输出
view
plain
int _tmain(int argc, _TCHAR* argv[])
{
CRecvEventClass rec1, rec2;
int iIn, iOut;
iIn = 10;
iOut = rec1.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 1, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 30;
iOut = rec1.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 2, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 60;
iOut = rec2.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 3, Init:%d, Result:%d\n"), iIn, iOut);
TCHAR c;
_tscanf(_T("%c"), &c);
return 0;
}
输出结果为:
view
plain
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 1, Init:10, Result:70
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 2, Init:30, Result:90
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 3, Init:60, Result:120
从输出结果上看事件是被顺次调用了,至于程序中的变量值,主要表明这些事件执行的而动作是对应于实例的,不是对应于类的。
2、 实现的要点
(1)、事件触发类的实现要点
a、 事件触发类必须要定义接收事件处理的接口类INotifyClassEvent
b、 事件触发类要定义INotifyClassEvent接口类的成员变量
view
plain
INotifyClassEvent *m_pncRecvEvent;
当需要触发事件时通过这个接口类调用相应的函数
c、 或者把上面的INotifyClassEvent接口类的成员变量直接公开被外部修改,或者使用类似RegisterEvent和UnRegisterEvent的方法提供对这些成员的修改,外部事件接收类才能接收事件
d、 在工作时,需要触发事件的地方,通过调用INotifyClassEvent接口类来事件通知事件接收类
(2)、事件接收对象类或事件处理对象类的实现要点
a、 事件接收类必须继承或多重继承自接口类INotifyClassEvent
b、 使用自身的指针this作为参数调用事件触发类的RegisterEvent和UnRegisterEvent的方法,或者直接修改事件触发类的相应成员(如果提供了访问权限)
d、 在事件处理方法中当做普通的成员函数进行编写,然后在工作中,当有事件触发时就可以得到通知被调用。
3、 优缺点
(1)、优点
a、无论事件触发类还是接收事件处理类开发简单,代码量少、简洁
b、数据安全性高,无论对于事件处理函数和接收事件处理类实例都不存在类型转换,这两个数据都可以在编译期间得到类型检查
(2)、缺点
a、不能部分接收消息事件,要实现接口类的所有函数,虽然可以实现时什么都不做,但是需要都实现,为了让事件接收类可以选择接收的事件,事件触发类开发者必须拆分分类成多个事件接口,这样事件接收类可以选择继承,就可以只接收部分消息了,但是如果量太大,事件接收类需要继承的接口列表可能很多,增加了复杂度。
b、接收事件处理方法由于是继承自接口,所以其方法属性必须是public方法,对于事件接收类的封装造成了一定的破坏。
三、静态函数方式实现多对象事件接收
对应的例子工程名MultiObjectEvent
在静态函数模拟事件方法中,如果读者细心的话,会发现类CNotifyClass只包含一个指定对象的成员变量m_pEventParameter,在使用RegisterEvent方法注册接收事件的函数时如果不接收某些事件,就把相应的函数参数设为NULL,但是接收事件的对象参数只能是一个,这样就不能有多个类CRecvEventClass的实例接收事件,但是有的时候因为类CNotifyClass是一个服务类,就可能存在多个接收类的实例同时接收不同的事件,这个时候在静态函数模拟事件方法中将无能为力,所以要实现多个类对象能接收事件,就必须分开注册方法RegisterEvent,同时允许注册不同的pParameter,这个的具体实现代码在MultiObjectEvent例子中,我们看看类CNotifyClass的声明
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void *);
typedef int (*PEVENT_NOPARAM_RETURN)(void *);
typedef void (*PEVENT_PARAM_NORETURN)(void *, int);
typedef int (*PEVENT_PARAM_RETURN)(void *, int);
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc, void *pParameter);
void UnRegisterNoParamNoRetEvent();
bool RegisterEvent(PEVENT_NOPARAM_RETURN pFunc, void *pParameter);
void UnRegisterNoParamRetEvent();
bool RegisterEvent(PEVENT_PARAM_NORETURN pFunc, void *pParameter);
void UnRegisterParamNoRetEvent();
bool RegisterEvent(PEVENT_PARAM_RETURN pFunc, void *pParameter);
void UnRegisterParamRetEvent();
void UnRegisterAllEvent();
int DoNotifyEventWork();
protected:
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
PEVENT_NOPARAM_RETURN m_pNoParam_Return_EventHandler;
PEVENT_PARAM_NORETURN m_pParam_NoReturn_EventHandler;
PEVENT_PARAM_RETURN m_pParam_Return_EventHandler;
protected:
void *m_pNoParamNoRetEventParameter;
void *m_pNoParamRetEventParameter;
void *m_pParamNoRetEventParameter;
void *m_pParamRetEventParameter;
};
分开事件的注册,同时允许不同的参数传递进入,保存在多个参数成员变量中,来实现多个不同类对象接收事件的方法,可以看到存在大量的RegisterEvent和UnRegisterParamRetEvent方法,正是由于这种方法代码量比较大,书写非常麻烦,容易出错,而且后面介绍的方法更好,所以不推荐这种方法,这里就不详述了,有兴趣的读者自己去看例子程序。
读后感:在我们一个项目中,看前辈的代码,看到FastDelegate.h和FastDelegateBind.h,直接告诉我能在此中学到很多东西,也想把此种道理弄明白,会调用。但是阅读了一下代码,都是模板等,汗颜 没有看懂 ,就上网搜 一直搜到此篇文档,原来这是委托的一种实现,从而学到了很多东西。知道了什么叫“委托”,原来并不是我认为的注册回调函数可以不用static成员函数或者全局函数这么一个好处。
作者:gogogo
关键字: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的多个实例时就会有些麻烦,为了排除这些麻烦或者说规范这些问题,本文后续章节会提供多种方式来模拟委托事件方式。
本文谈到的所有例子代码我都已经上传,具体下载地址是http://download.csdn.net/detail/gogogo/3830459,所有程序在Visual studio 2005平台调试通过,对于本文及例子中提到的一些主要类如下:事件触发对象类叫做CNotifyClass,事件接收对象类或事件处理对象类叫做CRecvEventClass、CRecvEventClassOne等等。
例子代码被修正过,新的下载地址被更新了,并且是免资源分下载的了
下边来看具体的实现方案
一、静态函数模拟事件
对应的例子工程名StaticFunctionEvent
1、 具体的实现方法
(1)、事件触发对象类CNotifyClass的类定义如下:
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void *);
typedef int (*PEVENT_NOPARAM_RETURN)(void *);
typedef void (*PEVENT_PARAM_NORETURN)(void *, int);
typedef int (*PEVENT_PARAM_RETURN)(void *, int);
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter);
void UnRegisterEvent();
void DoNotifyEventWork();
protected:
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
PEVENT_NOPARAM_RETURN m_pNoParam_Return_EventHandler;
PEVENT_PARAM_NORETURN m_pParam_NoReturn_EventHandler;
PEVENT_PARAM_RETURN m_pParam_Return_EventHandler;
protected:
void *m_pEventParameter;
};
类实现如下:
view
plain
#include "NotifyClass.h"
CNotifyClass::CNotifyClass(void)
: m_pEventParameter(NULL)
, m_pNoParam_NoReturn_EventHandler(NULL)
, m_pNoParam_Return_EventHandler(NULL)
, m_pParam_NoReturn_EventHandler(NULL)
, m_pParam_Return_EventHandler(NULL)
{
}
CNotifyClass::~CNotifyClass(void)
{
UnRegisterEvent();
}
bool
CNotifyClass::RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc1, PEVENT_NOPARAM_RETURN pFunc2,
PEVENT_PARAM_NORETURN pFunc3, PEVENT_PARAM_RETURN pFunc4, void *pParameter)
{
if (NULL != m_pNoParam_NoReturn_EventHandler)
return false;
m_pEventParameter = pParameter;
m_pNoParam_NoReturn_EventHandler = pFunc1;
m_pNoParam_Return_EventHandler = pFunc2;
m_pParam_NoReturn_EventHandler = pFunc3;
m_pParam_Return_EventHandler = pFunc4;
return true;
}
void
CNotifyClass::UnRegisterEvent()
{
m_pEventParameter = NULL;
m_pNoParam_NoReturn_EventHandler = NULL;
m_pNoParam_Return_EventHandler = NULL;
m_pParam_NoReturn_EventHandler = NULL;
m_pParam_Return_EventHandler = NULL;
}
void
CNotifyClass::DoNotifyEventWork()
{
int iResult = 0;
if (m_pNoParam_NoReturn_EventHandler != NULL)
m_pNoParam_NoReturn_EventHandler(m_pEventParameter);
if (m_pNoParam_Return_EventHandler != NULL)
iResult = m_pNoParam_Return_EventHandler(m_pEventParameter);
iResult = iResult + 10;
if (m_pParam_Return_EventHandler != NULL)
iResult = m_pParam_Return_EventHandler(m_pEventParameter, iResult);
iResult = iResult + 10;
if (m_pParam_NoReturn_EventHandler != NULL)
m_pParam_NoReturn_EventHandler(m_pEventParameter, iResult);
}
类CNotifyClass声明时要提供类似如下的事件处理函数声明,所有接收事件的对象都要使用如下的函数声明来定义函数
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void*);
事件接收对象类通过调用RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,当会触发消息的工作方法DoNotifyEventWork被调用时,会检测哪些事件被注册了,被注册了就调用,就如同进行了事件通知。
(2)、事件接收对象类或事件处理对象类CRecvEventClass的类定义如下:
view
plain
class CRecvEventClass
{
public:
CRecvEventClass(void);
~CRecvEventClass(void);
public:
int DoWork(int iArg);
protected:
static void OnNoParamNoReturnEvent(void *pvParam);
static int OnNoParamReturnEvent(void *pvParam);
static void OnParamNoReturnEvent(void *pvParam, int iArg);
static int OnParamReturnEvent(void *pvParam, int iArg);
protected:
CNotifyClass m_ncNotify;
int m_nNum;
};
类实现如下:
view
plain
#include "RecvEventClass.h"
CRecvEventClass::CRecvEventClass(void)
{
m_ncNotify.RegisterEvent(OnNoParamNoReturnEvent, OnNoParamReturnEvent, OnParamNoReturnEvent, OnParamReturnEvent, this);
}
CRecvEventClass::~CRecvEventClass(void)
{
m_ncNotify.UnRegisterEvent();
}
int
CRecvEventClass::DoWork(int iArg)
{
m_nNum = iArg;
m_ncNotify.DoNotifyEventWork();
return m_nNum;
}
void
CRecvEventClass::OnNoParamNoReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
}
}
int
CRecvEventClass::OnNoParamReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
return p->m_nNum;
}
else
return 0;
}
void
CRecvEventClass::OnParamNoReturnEvent(void *pvParam, int iArg)
{
_tprintf(_T("Run OnParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = iArg + 10;
}
}
int
CRecvEventClass::OnParamReturnEvent(void *pvParam, int iArg)
{
_tprintf(_T("Run OnParamReturnEvent\n"));
int iRet = iArg + 10;
return iRet;
}
这里事件接收对象类或事件处理对象类CRecvEventClass在定义时一定要定义同事件触发类CNotifyClass提供的方法声明相同的方法来接收处理事件,由于CNotifyClass定义的方法声明是普通的指向函数指针,不包含对象的this指针传递,所以这种方法不能是实例成员函数,只能是静态成员方法,就如同
view
plain
static void OnNoParamNoReturnEvent(void *pvParam);
当定义了事件处理方法后,可以通过调用类CNotifyClass的方法RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,本例是在构造和析构函数中调用的,当CNotifyClass类的DoNotifyEventWork方法被调用时,就会通过函数指针事件通知到类CRecvEventClass,在事件处理方法中为了能区分CRecvEventClass的不同实例,所以要在类CNotifyClass的方法RegisterEvent中提供了pParameter参数,通常CRecvEventClass在调用RegisterEvent时传递对象本身的this指针,这样在事件处理方法中就可以通过pvParam参数得到这个指针的类实例的this指针,就像OnNoParamNoReturnEvent中的代码
view
plain
CRecvEventClass::OnNoParamNoReturnEvent(void *pvParam)
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
if (pvParam != NULL)
{
CRecvEventClass *p = reinterpret_cast<CRecvEventClass *>(pvParam);
p->m_nNum = p->m_nNum + 10;
}
}
可以直接将pvParam参数转化为CRecvEventClass类实例的指针,然后就像成员方法一样访问类实例的其他方法和成员了。
(3)、使用的例子及输出
view
plain
int _tmain(int argc, _TCHAR* argv[])
{
//2个对象,虽然类里用的静态方法,但是不会影响到不同对象的调用,互相不会干扰其他的对象的状态
CRecvEventClass rec1, rec2;
int iIn, iOut;
iIn = 10;
iOut = rec1.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 1, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 30;
iOut = rec1.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 2, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 60;
iOut = rec2.DoWork(iIn);
_tprintf(_T("StaticFunctionEvent test 3, Init:%d, Result:%d\n"), iIn, iOut);
TCHAR c;
_tscanf(_T("%c"), &c);
return 0;
}
输出结果为:
view
plain
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 1, Init:10, Result:70
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 2, Init:30, Result:90
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
StaticFunctionEvent test 3, Init:60, Result:120
从输出结果上看事件是被顺次调用了,至于程序中的变量值,主要表明这些事件执行的而动作是对应于实例的,不是因为是静态方法就是对应于类的。
2、 实现的要点
(1)、事件触发类的实现要点
a、 事件触发类必须要定义要处理事件的函数声明
b、 事件触发类要定义相应函数声明类型的成员变量
view
plain
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
c、 为了能传递事件接收类的实例,要定义事件接收类的实例指针
view
plain
void *m_pEventParameter;
d、 或者把上面的函数指针成员和事件接收类的实例指针直接公开被外部修改,或者使用类似RegisterEvent和UnRegisterEvent的方法提供对这些成员的修改,外部事件接收类才能接收事件
e、 在工作时,需要触发事件的地方,通过调用函数指针成员来事件通知事件接收类
(2)、事件接收对象类或事件处理对象类的实现要点
a、 使用静态方法定义与实现满足事件接收函数声明的方法
b、 使用这些静态方法及自身作为参数调用事件触发类的RegisterEvent和UnRegisterEvent的方法,或者直接修改事件触发类的相应成员(如果提供了访问权限)
c、 在这些静态方法中,转换参数为this,然后进行工作,当有事件触发时就可以得到通知被调用。
3、 优缺点
(1)、优点
a、可以部分接收事件,如果不接收什么事件,只需要在调用RegisterEvent时传递NULL给对应的事件函数就可以
b、事件处理方法不需要必须是public的方法,任意访问类别都可以
(2)、缺点
类型安全度相对较底,需要类型转换,不容易在编译期间发现问题,这里主要是指传递的参数pParameter,对于事件处理函数的指针是类型安全的
二、接口模拟事件
对应的例子工程名InterfaceEvent
由于C++不支持接口,但是支持抽象类和多重继承,所以可以通过class或struct模拟实现接口。
1、 具体的实现方法
(1)、事件触发对象类CNotifyClass的类定义如下:
view
plain
class INotifyClassEvent
{
public:
INotifyClassEvent() {};
virtual ~INotifyClassEvent() {};
public:
virtual void OnNoParamNoReturnEvent() = 0;
virtual int OnNoParamReturnEvent() = 0;
virtual void OnParamNoReturnEvent(int) = 0;
virtual int OnParamReturnEvent(int) = 0;
};
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(INotifyClassEvent *pncRecvEvent);
void UnRegisterEvent();
void DoNotifyEventWork();
protected:
INotifyClassEvent *m_pncRecvEvent;
};
类实现如下:
view
plain
#include "NotifyClass.h"
CNotifyClass::CNotifyClass(void)
: m_pncRecvEvent(NULL)
{
}
CNotifyClass::~CNotifyClass(void)
{
UnRegisterEvent();
}
bool
CNotifyClass::RegisterEvent(INotifyClassEvent *pncRecvEvent)
{
if (NULL != m_pncRecvEvent)
return false;
m_pncRecvEvent = pncRecvEvent;
return true;
}
void
CNotifyClass::UnRegisterEvent()
{
m_pncRecvEvent = NULL;
}
void
CNotifyClass::DoNotifyEventWork()
{
if (NULL == m_pncRecvEvent)
return;
int iResult = 0;
m_pncRecvEvent->OnNoParamNoReturnEvent();
iResult = m_pncRecvEvent->OnNoParamReturnEvent();
iResult = iResult + 10;
iResult = m_pncRecvEvent->OnParamReturnEvent(iResult);
iResult = iResult + 10;
m_pncRecvEvent->OnParamNoReturnEvent(iResult);
}
类CNotifyClass声明时要定义事件处理函数集的接口INotifyClassEvent,所有接收事件的类都要继承并实现这个接口来接收事件。
事件接收对象类通过调用RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,当会触发事件的工作方法DoNotifyEventWork被调用时,会检测哪些事件被注册了,被注册了就调用,就如同进行了事件通知。
(2)、事件接收对象类或事件处理对象类CRecvEventClass的类定义如下:
view
plain
class CRecvEventClass : public INotifyClassEvent
{
public:
CRecvEventClass(void);
~CRecvEventClass(void);
public:
int DoWork(int iArg);
protected:
virtual void OnNoParamNoReturnEvent();
virtual int OnNoParamReturnEvent();
virtual void OnParamNoReturnEvent(int iArg);
virtual int OnParamReturnEvent(int iArg);
protected:
CNotifyClass m_ncNotify;
int m_nNum;
};
类实现如下:
view
plain
#include "RecvEventClass.h"
CRecvEventClass::CRecvEventClass(void)
{
m_ncNotify.RegisterEvent(this);
}
CRecvEventClass::~CRecvEventClass(void)
{
m_ncNotify.UnRegisterEvent();
}
int
CRecvEventClass::DoWork(int iArg)
{
m_nNum = iArg;
m_ncNotify.DoNotifyEventWork();
return m_nNum;
}
void
CRecvEventClass::OnNoParamNoReturnEvent()
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
m_nNum = m_nNum + 10;
}
int
CRecvEventClass::OnNoParamReturnEvent()
{
_tprintf(_T("Run OnNoParamReturnEvent\n"));
m_nNum = m_nNum + 10;
return m_nNum;
}
void
CRecvEventClass::OnParamNoReturnEvent(int iArg)
{
_tprintf(_T("Run OnParamNoReturnEvent\n"));
m_nNum = iArg + 10;
}
int
CRecvEventClass::OnParamReturnEvent(int iArg)
{
int iRet;
_tprintf(_T("Run OnParamReturnEvent\n"));
iRet = iArg + 10;
return iRet;
}
这里事件接收对象类或事件处理对象类CRecvEventClass在定义时一定要继承事件通知接口INotifyClassEvent,通过实现这个接口的成员函数来接收事件通知。
当定义了事件处理方法后,可以通过调用类CNotifyClass的方法RegisterEvent和UnRegisterEvent注册接收消息或取消注册不再接收消息,本例是在构造和析构函数中调用的,当CNotifyClass类的DoNotifyEventWork方法被调用时,就会通过接口方法直接事件通知(调用)相应的事件处理方法
view
plain
void
CRecvEventClass::OnNoParamNoReturnEvent()
{
_tprintf(_T("Run OnNoParamNoReturnEvent\n"));
m_nNum = m_nNum + 10;
}
这里可以看到事件处理方法直接就是类成员方法,不需要传递对象的指针this。
(3)、使用的例子及输出
view
plain
int _tmain(int argc, _TCHAR* argv[])
{
CRecvEventClass rec1, rec2;
int iIn, iOut;
iIn = 10;
iOut = rec1.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 1, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 30;
iOut = rec1.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 2, Init:%d, Result:%d\n"), iIn, iOut);
iIn = 60;
iOut = rec2.DoWork(iIn);
_tprintf(_T("InterfaceEvent test 3, Init:%d, Result:%d\n"), iIn, iOut);
TCHAR c;
_tscanf(_T("%c"), &c);
return 0;
}
输出结果为:
view
plain
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 1, Init:10, Result:70
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 2, Init:30, Result:90
Run OnNoParamNoReturnEvent
Run OnNoParamReturnEvent
Run OnParamReturnEvent
Run OnParamNoReturnEvent
InterfaceEvent test 3, Init:60, Result:120
从输出结果上看事件是被顺次调用了,至于程序中的变量值,主要表明这些事件执行的而动作是对应于实例的,不是对应于类的。
2、 实现的要点
(1)、事件触发类的实现要点
a、 事件触发类必须要定义接收事件处理的接口类INotifyClassEvent
b、 事件触发类要定义INotifyClassEvent接口类的成员变量
view
plain
INotifyClassEvent *m_pncRecvEvent;
当需要触发事件时通过这个接口类调用相应的函数
c、 或者把上面的INotifyClassEvent接口类的成员变量直接公开被外部修改,或者使用类似RegisterEvent和UnRegisterEvent的方法提供对这些成员的修改,外部事件接收类才能接收事件
d、 在工作时,需要触发事件的地方,通过调用INotifyClassEvent接口类来事件通知事件接收类
(2)、事件接收对象类或事件处理对象类的实现要点
a、 事件接收类必须继承或多重继承自接口类INotifyClassEvent
b、 使用自身的指针this作为参数调用事件触发类的RegisterEvent和UnRegisterEvent的方法,或者直接修改事件触发类的相应成员(如果提供了访问权限)
d、 在事件处理方法中当做普通的成员函数进行编写,然后在工作中,当有事件触发时就可以得到通知被调用。
3、 优缺点
(1)、优点
a、无论事件触发类还是接收事件处理类开发简单,代码量少、简洁
b、数据安全性高,无论对于事件处理函数和接收事件处理类实例都不存在类型转换,这两个数据都可以在编译期间得到类型检查
(2)、缺点
a、不能部分接收消息事件,要实现接口类的所有函数,虽然可以实现时什么都不做,但是需要都实现,为了让事件接收类可以选择接收的事件,事件触发类开发者必须拆分分类成多个事件接口,这样事件接收类可以选择继承,就可以只接收部分消息了,但是如果量太大,事件接收类需要继承的接口列表可能很多,增加了复杂度。
b、接收事件处理方法由于是继承自接口,所以其方法属性必须是public方法,对于事件接收类的封装造成了一定的破坏。
三、静态函数方式实现多对象事件接收
对应的例子工程名MultiObjectEvent
在静态函数模拟事件方法中,如果读者细心的话,会发现类CNotifyClass只包含一个指定对象的成员变量m_pEventParameter,在使用RegisterEvent方法注册接收事件的函数时如果不接收某些事件,就把相应的函数参数设为NULL,但是接收事件的对象参数只能是一个,这样就不能有多个类CRecvEventClass的实例接收事件,但是有的时候因为类CNotifyClass是一个服务类,就可能存在多个接收类的实例同时接收不同的事件,这个时候在静态函数模拟事件方法中将无能为力,所以要实现多个类对象能接收事件,就必须分开注册方法RegisterEvent,同时允许注册不同的pParameter,这个的具体实现代码在MultiObjectEvent例子中,我们看看类CNotifyClass的声明
view
plain
typedef void (*PEVENT_NOPARAM_NORETURN)(void *);
typedef int (*PEVENT_NOPARAM_RETURN)(void *);
typedef void (*PEVENT_PARAM_NORETURN)(void *, int);
typedef int (*PEVENT_PARAM_RETURN)(void *, int);
class CNotifyClass
{
public:
CNotifyClass(void);
~CNotifyClass(void);
public:
bool RegisterEvent(PEVENT_NOPARAM_NORETURN pFunc, void *pParameter);
void UnRegisterNoParamNoRetEvent();
bool RegisterEvent(PEVENT_NOPARAM_RETURN pFunc, void *pParameter);
void UnRegisterNoParamRetEvent();
bool RegisterEvent(PEVENT_PARAM_NORETURN pFunc, void *pParameter);
void UnRegisterParamNoRetEvent();
bool RegisterEvent(PEVENT_PARAM_RETURN pFunc, void *pParameter);
void UnRegisterParamRetEvent();
void UnRegisterAllEvent();
int DoNotifyEventWork();
protected:
PEVENT_NOPARAM_NORETURN m_pNoParam_NoReturn_EventHandler;
PEVENT_NOPARAM_RETURN m_pNoParam_Return_EventHandler;
PEVENT_PARAM_NORETURN m_pParam_NoReturn_EventHandler;
PEVENT_PARAM_RETURN m_pParam_Return_EventHandler;
protected:
void *m_pNoParamNoRetEventParameter;
void *m_pNoParamRetEventParameter;
void *m_pParamNoRetEventParameter;
void *m_pParamRetEventParameter;
};
分开事件的注册,同时允许不同的参数传递进入,保存在多个参数成员变量中,来实现多个不同类对象接收事件的方法,可以看到存在大量的RegisterEvent和UnRegisterParamRetEvent方法,正是由于这种方法代码量比较大,书写非常麻烦,容易出错,而且后面介绍的方法更好,所以不推荐这种方法,这里就不详述了,有兴趣的读者自己去看例子程序。
读后感:在我们一个项目中,看前辈的代码,看到FastDelegate.h和FastDelegateBind.h,直接告诉我能在此中学到很多东西,也想把此种道理弄明白,会调用。但是阅读了一下代码,都是模板等,汗颜 没有看懂 ,就上网搜 一直搜到此篇文档,原来这是委托的一种实现,从而学到了很多东西。知道了什么叫“委托”,原来并不是我认为的注册回调函数可以不用static成员函数或者全局函数这么一个好处。
相关文章推荐
- 在C++中模拟委托事件的方法(四)
- 在C++中模拟委托事件的方法(六)
- 在C++中模拟委托事件的方法(五)
- 在C++中模拟委托事件的方法(六)
- 在C++中模拟委托事件的方法(一)
- 在C++中模拟委托事件的方法(二)
- 在C++中模拟委托事件的方法(四)
- 在C++中模拟委托事件的方法(一)
- 在C++中模拟委托事件的方法(二)
- 在C++中模拟委托事件的方法(下篇)
- 在C++中模拟委托事件的方法(三)
- 在C++中模拟委托事件的方法(五)
- C++中模拟委托事件的方法(三)
- 杂货边角(9):C++实现事件委托机制
- 成员函数指针与高性能的C++委托(上篇)
- 委托与事件(让类执行来自外部的方法)
- C#基础精华07(委托事件,委托的使用,匿名方法)
- C++事件机制(模拟C#的事件机制)
- C++实现事件委托机制
- C#与C++的区别(三) 委托与事件