C++ 事件机制实现-阿里云开发者社区

开发者社区> zhuweisky> 正文

C++ 事件机制实现

简介: 事件是面向组件开发的必要特性之一,但C++不直接支持事件,没关系,我自己实现了一个,感觉很好用,分享给大家!    最开始打算用函数指针模拟事件,但由于C++中成员函数指针不能和void*相互强转,而且 typedef中不能含有模板,所以才不得已以接口继承实现。
+关注继续查看

    事件是面向组件开发的必要特性之一,但C++不直接支持事件,没关系,我自己实现了一个,感觉很好用,分享给大家!
    最开始打算用函数指针模拟事件,但由于C++中成员函数指针不能和void*相互强转,而且 typedef中不能含有模板,所以才不得已以接口继承实现。这样效果也不错 :)

一. 先看看事件接口定义和实现

#ifndef IEVENT_H
#define IEVENT_H

/*
 以下各基础设施是在C++中事件机制的完整实现,事件是面向组件开发的必要特性之一。
  
 创 作 者:sky
 时    间:2005.06.22 
 修订时间:2005.06.22
*/

#include 
"../Collection/SafeArrayList.h"
template
<class SenderType ,class ParaType> class EventPublisher ;

class NullType
{
};

// IEventHandler  是事件处理句柄,预定事件的类从此接口继承以实现事件处理函数
template<class SenderType ,class ParaType>
interface IEventHandler
{

public:
 
virtual ~IEventHandler(){}
private:
 
virtual void HandleEvent(SenderType sender ,ParaType para)  = 0 ;
 friend 
class EventPublisher<SenderType ,ParaType> ;
};

// IEvent 事件预定方通过此接口预定事件
template<class SenderType ,class ParaType>
interface IEvent
{
public:

 
virtual ~IEvent(){}
 
virtual void Register  (IEventHandler<SenderType ,ParaType>* handler) = 0 ;  
 
virtual void UnRegister(IEventHandler<SenderType ,ParaType>* handler) = 0 ;
};

// IEventActivator 事件发布方通过此接口触发事件
template<class SenderType ,class ParaType>
interface IEventActivator
{
public:

 
virtual ~IEventActivator(){}
 
virtual void Invoke(SenderType sender ,ParaType para) = 0;
 
virtual int  HandlerCount() = 0;
 
virtual IEventHandler<SenderType ,ParaType>* GetHandler(int index) = 0;
};

// IEventPublisher 事件发布方发布事件相当于就是发布一个IEventPublisher派生类的对象
// 不过仅仅将该对象的IEvent接口发布即可。
template<class SenderType ,class ParaType>
interface IEventPublisher : public IEvent<SenderType ,ParaType> ,public IEventActivator<SenderType ,ParaType> 
{
};

// EventPublisher是IEventPublisher的默认实现
template<class SenderType ,class ParaType>
class EventPublisher :public IEventPublisher<SenderType ,ParaType>
{
private:
 SafeArrayList
< IEventHandler<SenderType ,ParaType> > handerList ;
 IEventHandler
<SenderType ,ParaType>* innerHandler ;

public:
 
void Register(IEventHandler<SenderType ,ParaType>* handler) 
 {
  
this->handerList.Add(handler) ;
 }

 
void UnRegister(IEventHandler<SenderType ,ParaType>* handler)
 {
  
this->handerList.Remove(handler) ;
 }

 
void Invoke(SenderType sender ,ParaType para)
 {
  
int count = this->handerList.Count() ;
  
for(int i=0 ;i<count ;i++)
  {
   IEventHandler
<SenderType ,ParaType>* handler = this->handerList.GetElement(i) ;
   handler
->HandleEvent(sender ,para) ;
  }
 } 

 
int  HandlerCount()
 {
  
return this->handerList.Count() ;
 }

 IEventHandler
<SenderType ,ParaType>* GetHandler(int index)
 {
  
return this->handerList.GetElement(index) ;
 }
};

#endif

    上面的实现是浅显易懂的,关键是要注意IEventPublisher的双重身份-- 事件发布方最好发布IEvent指针给外部,而该指针实际指向的是一个EventPublisher对象,这是为了避免外部直接调用IEventActivator接口的方法。

二. 一个定时器类Timer,演示如何发布事件。想必大家都知道定时器的用途了哦,这个Timer就像C#中的Timer类一样。

#ifndef TIMER_H
#define TIMER_H
/*
 Timer 定时器,每经过一段指定时间就触发事件
 
 创 作 者:sky
 时    间:2005.06.22 
 修订时间:2005.06.22
*/
#include 
"IEvent.h"
#include 
"Thread.h"

void TimerThreadStart(void* para) ;

class Timer
{
private:
 
int spanInMillSecs ;
 
volatile bool isStop ;
 
volatile bool timerThreadDone ;
 
public:
 friend 
void TimerThreadStart(void* para) ;
 IEvent
<Timer* ,NullType>* TimerTicked ;

 Timer(
int span_InMillSecs) 
 {
  
this->isStop = true ;
  
this->timerThreadDone = true ;
  
this->spanInMillSecs = span_InMillSecs ;
  
this->TimerTicked = new EventPublisher<Timer* ,NullType> ;
 }
 
 
~Timer()
 {
  
this->Stop() ;
  delete 
this->TimerTicked ;
 } 

 
void Start()
 {
  
if(! this->isStop)
  {
   
return ;
  }

  
this->isStop = false ;
  Thread thread ;
  thread.Start(TimerThreadStart ,
this) ;
  
//unsigned int  dwThreadId ;
  
//HANDLE hThread = (HANDLE)_beginthreadex(NULL, 0 , (unsigned int (_stdcall*)(void*))&TimerThreadStart , this, 0, &dwThreadId);   
  
 }

 
void Stop()
 {
  
ifthis->isStop)
  {
   
return ;
  }
  
  
this->isStop = true ;

  
//等待工作线程退出
  while(! this->timerThreadDone)
  {
   Sleep(
200) ;
  }
 }

private
 
void WorkerThread()
 {
  
this->timerThreadDone = false ;

  
while(! this->isStop)
  {  
   Sleep(
this->spanInMillSecs) ;

   
if(this->isStop)
   {
    
break ;
   }
   
   NullType nullObj ;
   ((EventPublisher
<Timer* ,NullType>*)this->TimerTicked)->Invoke(this ,nullObj) ;
  }

  
this->timerThreadDone = true ;
 }
};

void TimerThreadStart(void* para)
{
    Timer
* timer = (Timer*)para ;
    timer
->WorkerThread() ;
}
#endif

    上面的示例清晰地说明了如何发布一个事件,如何在适当的时候触发事件,接下来看看如何预定事件。

三. 预定事件例子

class TimerEventExample  :public IEventHandler<Timer* ,NullType> ,CriticalSection
{
private:
 Timer
* timer ;
 
public:
 TimerEventExample(
int checkSpan) 
 {  
  
this->timer = new Timer(checkSpan) ;
  
this->timer->TimerTicked->Register(this) ;
 }

 
~TimerEventExample()
 {
  delete 
this->timer ;
 } 

private:
 
//处理定时事件
 void HandleEvent(Timer* sender ,NullType para)
 {
  cout
<<"Time ticked !"<<endl ;
 }
};

    到这里,已经将C++中的事件机制的实现及使用讲清楚了。C#提供了很多便利的基础设施来支持组件开发,而在C++中,这些基础设施需要自己动手来构建,在拥有了这些设施之后,相信使用C++进行组件开发会轻松一点。

     突然想起来,这里是.NET专区,这篇文章放在这里是否合适?犹豫了一下,还是决定留下来。也许可以对那些有C++基础的.NET开发者有所帮助,也算是对事件的内部机理的另一种展现吧。
 

 

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

相关文章
阿里云服务器怎么设置密码?怎么停机?怎么重启服务器?
如果在创建实例时没有设置密码,或者密码丢失,您可以在控制台上重新设置实例的登录密码。本文仅描述如何在 ECS 管理控制台上修改实例登录密码。
7238 0
[C#]委托和事件(详细讲解)
引言 委托 和 事件在 .Net Framework中的应用非常广泛,然而,较好地理解委托和事件对很多接触C#时间不长的人来说并不容易。它们就像是一道槛儿,过了这个槛的人,觉得真是太容易了,而没有过去的人每次见到委托和事件就觉得心里别(biè)得慌,混身不自在。
571 0
阿里云服务器端口号设置
阿里云服务器初级使用者可能面临的问题之一. 使用tomcat或者其他服务器软件设置端口号后,比如 一些不是默认的, mysql的 3306, mssql的1433,有时候打不开网页, 原因是没有在ecs安全组去设置这个端口号. 解决: 点击ecs下网络和安全下的安全组 在弹出的安全组中,如果没有就新建安全组,然后点击配置规则 最后如上图点击添加...或快速创建.   have fun!  将编程看作是一门艺术,而不单单是个技术。
8920 0
C# 委托 事件 匿名方法
(*) 委托 delegate 从最简单的例子开始: namespace ConsoleApplication1 { class Program { // 委托其实就相当于一个类型。这里,类型的名字叫BinaryOp public delegate int BinaryOp(int x, int y); static p
774 0
Linux平台用C++实现事件对象,同步线程
前文在Win32平台上用C++实现了事件对象Event,对线程进行同步,以达到期望目的。这次在Linux平台上实现与之类似的事件对象。与其相关的一组API包括:pthread_mutex_init,pthread_cond_init,pthread_mutex_lock,pthread_cond_wait,pthread_mutex_unlock,pthread_cond_broadcast,pthread_cond_timedwait,pthread_cond_destroy,pthread_mutex_destroy。
998 0
阿里云服务器如何登录?阿里云服务器的三种登录方法
购买阿里云ECS云服务器后如何登录?场景不同,阿里云优惠总结大概有三种登录方式: 登录到ECS云服务器控制台 在ECS云服务器控制台用户可以更改密码、更换系.
10522 0
阿里云ECS云服务器初始化设置教程方法
阿里云ECS云服务器初始化是指将云服务器系统恢复到最初状态的过程,阿里云的服务器初始化是通过更换系统盘来实现的,是免费的,阿里云百科网分享服务器初始化教程: 服务器初始化教程方法 本文的服务器初始化是指将ECS云服务器系统恢复到最初状态,服务器中的数据也会被清空,所以初始化之前一定要先备份好。
5824 0
+关注
zhuweisky
从事软件开发行业十多年,专注于网络通信技术和网络语音视频技术,擅长系统架构设计、系统性能优化等。zhuweisky.cnblogs.com
300
文章
0
问答
文章排行榜
最热
最新
相关电子书
更多
《Nacos架构&原理》
立即下载
《看见新力量:二》电子书
立即下载
云上自动化运维(CloudOps)白皮书
立即下载