zoukankan      html  css  js  c++  java
  • 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()
     {
      
    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开发者有所帮助,也算是对事件的内部机理的另一种展现吧。
     

  • 相关阅读:
    10月日常练习1题目描述
    普及组复赛历年考题
    9.3练习题7 子串乘积正负分类 题解
    9.3练习题6 旅行 题解
    9.3练习题4 语句解析 题解
    9.3练习题5 单词覆盖还原 题解
    unity
    矩阵快速幂
    点权和
    SCOI生日快乐
  • 原文地址:https://www.cnblogs.com/zhuweisky/p/234152.html
Copyright © 2011-2022 走看看