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