C++实现事件机制
2015-06-03 13:53
387 查看
http://blog.csdn.net/linzhengqun/article/details/1913236
http://www.cppblog.com/weiym/archive/2013/01/31/197649.html
委托是一种很实用的设计方法,一个模块可以将某些事情委托给其他实体去做,而对于模块本身不需要知道受委托的实体是什么,它只知道这个实体遵循某种接口规范。回调函数可以认为是一种委托,它在Windows编程中起了非常重要的作用。
委托的一个重要应用是事件机制,假设有类A负责加载数据,类B用于实时显示A的加载进度,那么A必须向B引发一些事件,以表明它的加载进度。要实现这种机制可以用观察者模式,Java即使用观察者模式来实现事件监听的。Delphi使用了类似回调函数的技术来实现事件,这样也有一些好处,就是简单高效,对于一些轻量级的应用还是非常合适的。
C++如何实现事件,当然可以用观察者模式来实现,不过这里要介绍另一种方法,就是用成员函数指针,这种方法更类似于Delphi的事件,优点是简单高效。
下面是我写的两个源文件,对通用事件提供了支持,其中涉及到成员函数指针的知识,我就不班门弄斧了,直接给就出源代码如下:
EventUtils.h
#ifndef EVENTUTILS_H_
#define EVENTUTILS_H_
// 用于欺骗编译器,传递This指针
class CMemFunObj
{
};
// 通用函数类型
typedef void (CMemFunObj:: *PFNMEMFUN)();
// 成员函数结构
typedef struct tagMEMBERFUN {
CMemFunObj *Self;
PFNMEMFUN pfnAddr;
} MEMBERFUN, *PMEMBERFUN;
// 生成成员函数结构
MEMBERFUN MakeMemberFun(CMemFunObj *Self, PFNMEMFUN pfnAddr);
// 宏:生成成员函数结构
#define MAKEMEMFUN(Self, pfnAddr) /
MakeMemberFun((CMemFunObj*)Self, (PFNMEMFUN)pfnAddr)
// 宏:回调成员函数,FunType为具体函数类型,MemFun为成员函数结构
#define CALLMEMFUN(FunType, MemFun) /
(MemFun.Self->*(FunType)MemFun.pfnAddr)
// 宏:判断成员函数结构是否有值
#define ISMEMFUNASSIGNED(MemFun) /
(MemFun.Self != NULL) && (MemFun.pfnAddr != NULL)
#endif // EVENTUTILS_H_
EventUtils.cpp
#include "EventUtils.h"
// 生成成员函数结构
MEMBERFUN MakeMemberFun(CMemFunObj *Self, PFNMEMFUN pfnAddr)
{
MEMBERFUN Memfun;
Memfun.pfnAddr = pfnAddr;
Memfun.Self = Self;
return Memfun;
}
其中比较有意思的是用CMemFunObj来做对象绑定,这个类会欺骗编译器,使编译将This指针传进成员函数;MEMBERFUN是成员函数结构,一个成员函数要成功调用必须有两个要素,一个是绑定的对象,一个是函数地址,这就是MEMBERFUN的内容。
下面看看如何用这个单元,有一个CRunner类,提供一个Run方法,我们要实现的是监控Run的进度。
首先声明CRunner,声明进度事件类型,以及在CRunner保存一个事件类型的成员:
#ifndef RUNNER_H_
#define RUNNER_H_
#include "EventUtils.h"
// 进度事件类型
typedef void (CMemFunObj:: *RUNPROCESS)(int nPercent);
class CRunner
{
public:
CRunner();
virtual ~CRunner();
// 设置事件结构
void SetOnProcess(MEMBERFUN OnRunProcess);
// 开始运行
void Run();
private:
MEMBERFUN m_OnRunProcess;
};
#endif // RUNNER_H_
接着实现CRunner,并看看Run如何调用事件:
#include "Runner.h"
#include <string.h>
#include <windows.h>
CRunner::CRunner()
{
memset(&m_OnRunProcess, 0, sizeof(m_OnRunProcess));
}
CRunner::~CRunner(){}
void CRunner::SetOnProcess( MEMBERFUN OnRunProcess )
{
m_OnRunProcess = OnRunProcess;
}
void CRunner::Run()
{
int nTime = 0;
while ((nTime++) < 100)
{
Sleep(10);
if (ISMEMFUNASSIGNED(m_OnRunProcess))
CALLMEMFUN(RUNPROCESS, m_OnRunProcess)(nTime);
}
}
Run函数用ISMEMFUNASSIGNED宏判断m_OnRunProcess是否被赋值,如果有,则用CALLMEMFUN宏来调用具体的事件。关于这几个宏,可以参考EventUtils.h
CRunner支持事件之后,来看看事件如何被接收:
#include "EventUtils.h"
#include "Runner.h"
#include <iostream>
using namespace std;
class EventSink
{
public:
void RunProcess(int Percent)
{
cout<<Percent<<endl;
}
};
int main()
{
EventSink es;
CRunner R;
R.SetOnProcess(MAKEMEMFUN(&es, es.RunProcess));
R.Run();
return 0;
}
MAKEMEMFUN用于合成一个成员函数结构体,调用R.SetOnProcess之后,es便能够监听CRunner的进度事件。
使用这种技术来实现事件机制应该说是通用的,你可以整合到如MFC这类应用程序框架中。该技术的优点是高效,但缺点也很明显,就是只支持单点事件,如果要实现多点事件则要做更多的工作。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
http://www.cppblog.com/weiym/archive/2013/01/31/197649.html
委托是一种很实用的设计方法,一个模块可以将某些事情委托给其他实体去做,而对于模块本身不需要知道受委托的实体是什么,它只知道这个实体遵循某种接口规范。回调函数可以认为是一种委托,它在Windows编程中起了非常重要的作用。
委托的一个重要应用是事件机制,假设有类A负责加载数据,类B用于实时显示A的加载进度,那么A必须向B引发一些事件,以表明它的加载进度。要实现这种机制可以用观察者模式,Java即使用观察者模式来实现事件监听的。Delphi使用了类似回调函数的技术来实现事件,这样也有一些好处,就是简单高效,对于一些轻量级的应用还是非常合适的。
C++如何实现事件,当然可以用观察者模式来实现,不过这里要介绍另一种方法,就是用成员函数指针,这种方法更类似于Delphi的事件,优点是简单高效。
下面是我写的两个源文件,对通用事件提供了支持,其中涉及到成员函数指针的知识,我就不班门弄斧了,直接给就出源代码如下:
EventUtils.h
#ifndef EVENTUTILS_H_
#define EVENTUTILS_H_
// 用于欺骗编译器,传递This指针
class CMemFunObj
{
};
// 通用函数类型
typedef void (CMemFunObj:: *PFNMEMFUN)();
// 成员函数结构
typedef struct tagMEMBERFUN {
CMemFunObj *Self;
PFNMEMFUN pfnAddr;
} MEMBERFUN, *PMEMBERFUN;
// 生成成员函数结构
MEMBERFUN MakeMemberFun(CMemFunObj *Self, PFNMEMFUN pfnAddr);
// 宏:生成成员函数结构
#define MAKEMEMFUN(Self, pfnAddr) /
MakeMemberFun((CMemFunObj*)Self, (PFNMEMFUN)pfnAddr)
// 宏:回调成员函数,FunType为具体函数类型,MemFun为成员函数结构
#define CALLMEMFUN(FunType, MemFun) /
(MemFun.Self->*(FunType)MemFun.pfnAddr)
// 宏:判断成员函数结构是否有值
#define ISMEMFUNASSIGNED(MemFun) /
(MemFun.Self != NULL) && (MemFun.pfnAddr != NULL)
#endif // EVENTUTILS_H_
EventUtils.cpp
#include "EventUtils.h"
// 生成成员函数结构
MEMBERFUN MakeMemberFun(CMemFunObj *Self, PFNMEMFUN pfnAddr)
{
MEMBERFUN Memfun;
Memfun.pfnAddr = pfnAddr;
Memfun.Self = Self;
return Memfun;
}
其中比较有意思的是用CMemFunObj来做对象绑定,这个类会欺骗编译器,使编译将This指针传进成员函数;MEMBERFUN是成员函数结构,一个成员函数要成功调用必须有两个要素,一个是绑定的对象,一个是函数地址,这就是MEMBERFUN的内容。
下面看看如何用这个单元,有一个CRunner类,提供一个Run方法,我们要实现的是监控Run的进度。
首先声明CRunner,声明进度事件类型,以及在CRunner保存一个事件类型的成员:
#ifndef RUNNER_H_
#define RUNNER_H_
#include "EventUtils.h"
// 进度事件类型
typedef void (CMemFunObj:: *RUNPROCESS)(int nPercent);
class CRunner
{
public:
CRunner();
virtual ~CRunner();
// 设置事件结构
void SetOnProcess(MEMBERFUN OnRunProcess);
// 开始运行
void Run();
private:
MEMBERFUN m_OnRunProcess;
};
#endif // RUNNER_H_
接着实现CRunner,并看看Run如何调用事件:
#include "Runner.h"
#include <string.h>
#include <windows.h>
CRunner::CRunner()
{
memset(&m_OnRunProcess, 0, sizeof(m_OnRunProcess));
}
CRunner::~CRunner(){}
void CRunner::SetOnProcess( MEMBERFUN OnRunProcess )
{
m_OnRunProcess = OnRunProcess;
}
void CRunner::Run()
{
int nTime = 0;
while ((nTime++) < 100)
{
Sleep(10);
if (ISMEMFUNASSIGNED(m_OnRunProcess))
CALLMEMFUN(RUNPROCESS, m_OnRunProcess)(nTime);
}
}
Run函数用ISMEMFUNASSIGNED宏判断m_OnRunProcess是否被赋值,如果有,则用CALLMEMFUN宏来调用具体的事件。关于这几个宏,可以参考EventUtils.h
CRunner支持事件之后,来看看事件如何被接收:
#include "EventUtils.h"
#include "Runner.h"
#include <iostream>
using namespace std;
class EventSink
{
public:
void RunProcess(int Percent)
{
cout<<Percent<<endl;
}
};
int main()
{
EventSink es;
CRunner R;
R.SetOnProcess(MAKEMEMFUN(&es, es.RunProcess));
R.Run();
return 0;
}
MAKEMEMFUN用于合成一个成员函数结构体,调用R.SetOnProcess之后,es便能够监听CRunner的进度事件。
使用这种技术来实现事件机制应该说是通用的,你可以整合到如MFC这类应用程序框架中。该技术的优点是高效,但缺点也很明显,就是只支持单点事件,如果要实现多点事件则要做更多的工作。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
相关文章推荐
- NYOJ 76 超级台阶
- 在Golang中使用C语言代码实例
- NYOJ 36 最长公共子序列 (还是dp)
- NYOJ 37 回文字符串
- NYOJ 467 中缀式变后缀式
- NYOJ 44 子串和 (经典的dp问题)
- vc++6.0设定UNICODE编译环境
- vc++6.0配置和使用GDI+
- C++高级编程(第3版)
- Effective C++条款40
- c++ ofstream & ifstream文件流操作
- c++ ofstream & ifstream文件流操作
- struct字节对齐问题
- c++学习二
- C++学习一
- C语言学习心得
- c++中:和::的用途
- Program Pack
- java中调用c(c++)写的dll文件的实现及步骤(转)
- [erlang]erlang与C++的两种通讯方式