Posted on 2005-09-11 20:15
zhuweisky 閱讀(5715)
評(píng)論(5) 編輯 收藏 引用 所屬分類(lèi):
C++組件技術(shù)
事件是面向組件開(kāi)發(fā)的必要特性之一,但C++不直接支持事件,沒(méi)關(guān)系,我自己實(shí)現(xiàn)了一個(gè),感覺(jué)很好用,分享給大家!
最開(kāi)始打算用函數(shù)指針模擬事件,但由于C++中成員函數(shù)指針不能和void*相互強(qiáng)轉(zhuǎn),而且 typedef中不能含有模板,所以才不得已以接口繼承實(shí)現(xiàn)。這樣效果也不錯(cuò) :)
一. 先看看事件接口定義和實(shí)現(xiàn)
#ifndef IEVENT_H
#define IEVENT_H
/*
以下各基礎(chǔ)設(shè)施是在C++中事件機(jī)制的完整實(shí)現(xiàn),事件是面向組件開(kāi)發(fā)的必要特性之一。
創(chuàng) 作 者:sky
時(shí) 間:2005.06.22
修訂時(shí)間:2005.06.22
*/
#include "../Collection/SafeArrayList.h"
template<class SenderType ,class ParaType> class EventPublisher ;
class NullType
{
};
// IEventHandler 是事件處理句柄,預(yù)定事件的類(lèi)從此接口繼承以實(shí)現(xiàn)事件處理函數(shù)
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 事件預(yù)定方通過(guò)此接口預(yù)定事件
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 事件發(fā)布方通過(guò)此接口觸發(fā)事件
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 事件發(fā)布方發(fā)布事件相當(dāng)于就是發(fā)布一個(gè)IEventPublisher派生類(lèi)的對(duì)象
// 不過(guò)僅僅將該對(duì)象的IEvent接口發(fā)布即可。
template<class SenderType ,class ParaType>
interface IEventPublisher : public IEvent<SenderType ,ParaType> ,public IEventActivator<SenderType ,ParaType>
{
};
// EventPublisher是IEventPublisher的默認(rèn)實(shí)現(xiàn)
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
上面的實(shí)現(xiàn)是淺顯易懂的,關(guān)鍵是要注意IEventPublisher的雙重身份-- 事件發(fā)布方最好發(fā)布IEvent指針給外部,而該指針實(shí)際指向的是一個(gè)EventPublisher對(duì)象,這是為了避免外部直接調(diào)用IEventActivator接口的方法。
二. 一個(gè)定時(shí)器類(lèi)Timer,演示如何發(fā)布事件。想必大家都知道定時(shí)器的用途了哦,這個(gè)Timer就像C#中的Timer類(lèi)一樣。
#ifndef TIMER_H
#define TIMER_H
/*
Timer 定時(shí)器,每經(jīng)過(guò)一段指定時(shí)間就觸發(fā)事件
創(chuàng) 作 者:sky
時(shí) 間:2005.06.22
修訂時(shí)間: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
上面的示例清晰地說(shuō)明了如何發(fā)布一個(gè)事件,如何在適當(dāng)?shù)臅r(shí)候觸發(fā)事件,接下來(lái)看看如何預(yù)定事件。
三. 預(yù)定事件例子
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:
//處理定時(shí)事件
void HandleEvent(Timer* sender ,NullType para)
{
cout<<"Time ticked !"<<endl ;
}
};
到這里,已經(jīng)將C++中的事件機(jī)制的實(shí)現(xiàn)及使用講清楚了。C#提供了很多便利的基礎(chǔ)設(shè)施來(lái)支持組件開(kāi)發(fā),而在C++中,這些基礎(chǔ)設(shè)施需要自己動(dòng)手來(lái)構(gòu)建,在擁有了這些設(shè)施之后,相信使用C++進(jìn)行組件開(kāi)發(fā)會(huì)輕松一點(diǎn)。