C++ 事件机制实现

    事件是面向组件开发的必要特性之一,但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()
 {
  
if this -> 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开发者有所帮助,也算是对事件的内部机理的另一种展现吧。
 

你可能感兴趣的:(事件机制)