posts - 1,  comments - 4,  trackbacks - 0

    在 Java C #等语言或者说其类库中,都实现了事件模型。而 c++ 语言本身并没有定义事件机制,并且在目前众多优秀的 c++ 类库,包括 STL Boost 等都没有实现类似的事件机制。当我们被 MFC 的消息搞得头昏眼花之时,是否有冲动自己去实现一个简单的事件模型呢。我想,有着相同想法的人肯定很多,而真正动手来写可能会碰到各种各样的困难。下面就让我们一步步来编写一个简单的事件模型。

 

一、 了解事件模型的机制

在开始之前,我们有必要简单的了解一下事件模型的机制。事实上,事件模型的机制不止一种, Java c# 的事件机制就不太一样,不过事件模型的基础都是一样,那就是一般都使用 Observer 设计模式。关于 Observer 设计模式,希望详细了解的朋友可以参考《设计模式》一书,在这里我们就不详细的介绍,只是参照 c# 的事件机制来实现。

c# 中,我们可以以 event 关键字声明一个事件,然后我们可以将一个函数委托挂接在这个事件上,当事件被调用时,则所有挂接的函数也会被调用。这里有一个比较难以理解的词大概是委托,其实也不难理解。委托其实就是一种函数包装类,这种类可以把任何函数的指针保存起来,等到需要调用的时候再调用,并且这种类的实例必须能够挂接到事件之中。

看了上面这段话,是不是觉得其实事件模型也并不复杂。还想对事件模型了解得更多?我这里就不继续了,感兴趣的朋友可以查找一下相关的资料。下面我们就开始在 c++ 中编写一个简单的事件模型。

 

二、 设计一个简单的 c++ 事件模型

因为一个事件模型其实就是一个典型的 Observer 设计模式,因此最重要的就是 Subject( 目标类 ) Observer( 观察者类 ) 的设计。

首先,我们需要一个事件类,它其实是一个抽象的 Subject 。它是一个基类,所有的其他事件都从它继承,并且用户只需要从这个基类继承就可以自定义事件。我们不仿将这个类定义为 CEvent CEvent 的声明如下:

class CEvent  

{

public :

        

         typedef list < CEventHandler > data_type ;

 

         CEvent ();

 

         virtual ~ CEvent ();

 

         void operator ()()

         {

                  data_type :: iterator it ;

                                    

                   for ( it = m_observer . begin (); it != m_observer . end (); ++ it )

                   {

                            (* it )(* this );

                   }

         }

 

         CEvent & operator += ( const CEventHandler & handler )

         {

                  Register ( handler );

                   return * this ;

         }

 

         CEvent & operator -= ( const CEventHandler & handler )

         {

                  UnRegister ( handler );

                   return * this ;

         }

 

         void Register ( const CEventHandler & handler )

         {

                  m_observer . push_back ( handler );

         }

 

         void UnRegister ( const CEventHandler & handler )

         {

                  m_observer . remove ( handler );

         }

 

protected :

 

         // 将侦听的所有函数或者仿函数的集合起来

         data_type m_observer ;

};

 

其次,这个事件可以挂接任意的函数,也就是它可以保存所有的挂接函数。在 c++ 中,我们大致有三种类型的函数,全局或者静态函数、成员函数、仿函数。要在 CEvent 中实现一个或者多个方法来执行挂接的任务不太现实。我们参考 c# 的实现方法,可以先实现一个委托类,将各种函数保存起来。然后再使用 CEvent 对这个委托类进行挂接。我们不仿称这个类为 CEventHandler

然后,我们的这个委托类需要能够保存所有的函数。在 c++ 里,我们能够很容易的得到函数的指针,只需要在函数名前加 & ,但是各种函数,特别是成员函数它是没有类型的,这就要有一种机制,将所有的函数转换为同一种类型。因为函数是没有类型,也相当于每一个函数都是一种类型,解决这个问题的方法其实就是使用模板类。因此,我们先定义一个虚基类 CFunImpl 。对三种不同的函数,我们分别从 CfunImpl 继承三种不同的子类, CstaticFun CmemFun,CFunctor CstaticFun 表示全局的或者静态的函数,不过这必须是一个模板类,对于每一个静态或者全局函数,都是模板类的一个特化。类声明的代码如下:

template < typename Fun >

         class CStaticFun : public CFunImpl

         {

         public :

 

                  CStaticFun ( const Fun & fun ) : m_fun ( fun ){};

                  

                   virtual ~ CStaticFun ()

                   {

                   }

         protected :

                   Fun m_fun ;

         };

 

对于 CmemFun 类,我们如法炮制,不过我们这时需要保存两个成员变量,一个是成员函数的指针,另一个则是该成员函数所属的对象的指针。类声明的代码如下:

 

template < typename PointerToObj , typename PointerToMemFun >

         class CMemFun : public CFunImpl

         {

         public :

 

                  CMemFun ( const PointerToObj & pObj , PointerToMemFun pMemFn )

                            : m_pObj ( pObj ), m_pMemFun ( pMemFn )

                   {

                   }

 

virtual ~ CMemFun ()

                   {

                   }

                  

         protected :

                  PointerToObj m_pObj ;

                  PointerToMemFun m_pMemFun ;

};

 

对于仿函数,我们这里先不讲,因为仿函数使用得很少,并且这还涉及到仿函数的概念,有兴趣的朋友可以在阅读完本文之后自己来实现。

好,到现在为止,我们可以将所有的函数使用 CfunImpl 来表示了。因此,我们可以在委托类 CEventHandler 中保存 CfunImpl* 来达到我们的目的。 CEventHandler 的声明如下:

         class CEventHandler  

         {

         public :

                   virtual ~ CEventHandler ()

                   {

                            Clear ();

                   }

 

                  template < class Fun >

                  CEventHandler ( const Fun & fun )

                            : m_pImpl ( new CStaticFun < Fun >( fun ))

                   {}

                  

                  CEventHandler ( const CEventHandler & fun )

                            : m_pImpl ( NULL )

                   {

                            * this = fun ;

                   }

 

                   void Clear ()

                   {

                            if ( m_pImpl )

                            {

                                     delete m_pImpl ;

                                     m_pImpl = NULL ;

                            }

                   }

 

                  CEventHandler & operator = ( const CEventHandler & fun )

                   {

                            Clear ();

 

                            if ( fun . m_pImpl )

                            {

                                     m_pImpl = fun . m_pImpl -> Clone ();

                            }

 

                            return * this ;

                   }

                  

                   // 成员函数

                  template < typename PointerToObj , typename PointerToMemFun >

                            CEventHandler ( const PointerToObj & pObj , const PointerToMemFun & pMemFun )

                            : m_pImpl ( new CMemFun < PointerToObj , PointerToMemFun >( pObj , pMemFun ))

                   {}

 

                   void operator ()( CEvent & e )

                   {

if ( m_pImpl )

                            {

                                     (* m_pImpl )( e );

}

                   }

 

                   bool operator == ( const CEventHandler & handler )

                   {

                            if ( m_pImpl == NULL || handler . m_pImpl == NULL )

                            {

                                     return true ;

                            }

 

                            if ( typeid ( m_pImpl ) == typeid ( handler . m_pImpl ))

                            {

                                     return (* m_pImpl ) == (*( handler . m_pImpl ));

                            }

 

                            return false ;

                   }

 

         protected :

                  CFunImpl * m_pImpl ;

 

         };

 

不过要实现事件机制,我们还需要 CfunImpl* 的子类必须实现几个方法。

第一个方法就是调用函数的方法,我们这里要求重载 void operator()(CEvent& e); 来达到目的。

第二个方法就是 operator== ,为什么需要这个方法呢。因为对于 CEvent 来说,调用 UnRegister() 时,我们必须找到我们使用 Register()