用C++模拟C#事件机制
来源:优易学  2011-12-1 12:05:45   【优易学:中国教育考试门户网】   资料下载   IT书店
  C#中的事件机制可以很方便的实现设计模式中的Observer模式,C#提供了delegate 和 event 来实现这种机制,实际上只要delagate就可以实现event效果,event语法完全没必要,因为delegate是多播的。本文提供了一个C++版本的实现,与C#原生的事件机制相比,只有一点不同,我实现的delegate是单播的(为了避免delegate 和 event 功能重复的问题)。
  C# delegate 本质上是一个函数的面向对象的封装, 在C++语言中函数分好多种,包括 全局函数,成员函数,函数对象(即functor,虽然不是函数,但因为行为像函数,所以归为函数一类),青年人网提示因此在C++里实现delegate的关键就是封装上述3类函数的不同,对外提供一致的接口,先来看一下delegate的实现。
  template<class TReturn, class TArgument>
  class Delegate
  {
  public:
  Delegate(){}
  virtual ~Delegate(){}
  public:
  typedef TReturn (*InvokerType)(TArgument args);
  // for global or static methods
  Delegate(TReturn (*pCallback)(TArgument))
  :m_pInvoker(NULL)
  {
  Invoker<TReturn,TArgument>::Bind(pCallback);
  m_pInvoker = Invoker<TReturn,TArgument>::Invoke;
  }
  // for object member methods
  template<class TObject>
  Delegate(TObject* pObject, TReturn (TObject::*pCallback)(TArgument))
  :m_pInvoker(NULL)
  {
  MemberInvoker<TObject,TReturn,TArgument>::Bind(pObject,pCallback);
  m_pInvoker = MemberInvoker<TObject,TReturn,TArgument>::Invoke;
  }
  // for functor methods
  template<class TFunctor>
  Delegate(TFunctor* pCallback)
  :m_pInvoker(NULL)
  {
  FunctorInvoker<TFunctor,TReturn,TArgument>::Bind(pCallback);
  m_pInvoker = FunctorInvoker<TFunctor,TReturn,TArgument>::Invoke;
  }
  TReturn operator() (TArgument args)
  {
  return m_pInvoker(args);
  }
  //implementations
  private:
  InvokerType m_pInvoker;
  };
  delegate 本身就是一个函数对象,针对C++里3类函数提供了3个构造函数,每个构造函数的实现分别用到了一个辅助类别,分别是Invoker(用于全局函数或者类的静态函数),MemberInvoker(用于类成员函数),FunctorInvoker(用于functor) ,这三个辅助类别主要用于在编译时保存函数类型,对象类型等信息。实现如下:
  template<class TReturn, class TArgument>
  struct Invoker
  {
  typedef TReturn (*Method)(TArgument args);
  static TReturn Invoke(TArgument args)
  {
  return m_pCallback(args);
  }
  static void Bind(Method pCallback)
  {
  m_pCallback = pCallback;
  }
  private:
  static Method m_pCallback;
  };
  template<class TReturn, class TArgument>
  typename Invoker<TReturn,TArgument>::Method Invoker<TReturn,TArgument>::m_pCallback = NULL;
  template<class TObject, class TReturn, class TArgument>
  struct MemberInvoker
  {
  typedef TReturn (TObject::*MemberMethod)(TArgument);
  static TReturn Invoke(TArgument args)
  {
  return (m_pObject->*m_pCallback)(args);
  }
  static void Bind(TObject* pObject,MemberMethod pCallback)
  {
  m_pObject = pObject;
  m_pCallback = pCallback;
  }
  private:
  static TObject* m_pObject;
  static MemberMethod m_pCallback;
  };
  template<class TObject, class TReturn, class TArgument>
  TObject* MemberInvoker<TObject,TReturn,TArgument>::m_pObject = NULL;
  template<class TObject, class TReturn, class TArgument>
  typename MemberInvoker<TObject,TReturn,TArgument>::MemberMethod MemberInvoker<TObject,TReturn,TArgument>::m_pCallback = NULL;
  template<class TFunctor, class TReturn, class TArgument>
  struct FunctorInvoker
  {
  typedef TFunctor* FunctorMethod;
  static TReturn Invoke(TArgument args)
  {
  return m_pCallback(args);
  }
  static void Bind(FunctorMethod pCallback)
  {
  m_pCallback = pCallback;
  }
  private:
  static FunctorMethod m_pCallback;
  };
  template<class TFunctor, class TReturn, class TArgument>
  typename FunctorInvoker<TFunctor,TReturn,TArgument>::FunctorMethod FunctorInvoker<TFunctor,TReturn,TArgument>::m_pCallback = NULL;
  至此,一个完整的delegate实现完毕,我们可以用它来实现event 了。
  event实现如下,为了实现多播用std::list保存多个deledate:
  template< class TReturn, class TArgument>
  class Event
  {
  public:
  typedef TReturn ReturnValue;
  typedef TArgument EventArgs;
  typedef Delegate<TReturn, TArgument> EventHandler;
  public:
  Event(){}
  virtual ~Event(){}
  public:
  ReturnValue GetReturnValue(const EventHandler& rhs )
  {
  return m_ReturnValue[rhs];
  }
  ReturnValue operator() (EventArgs/* const& */rhs)
  {
  ReturnValue RetValue;
  for (std::list<EventHandler>::iterator i = m_Handler.begin(); i != m_Handler.end(); ++i)
  {
  RetValue = (*i)(rhs);
  m_ReturnValue[(*i)] = RetValue;
  }
  return RetValue;
  }
  Event& operator+= ( EventHandler/* const& */rhs )
  {
  m_Handler.push_back(rhs);
  return *this;
  }
  Event& operator-= ( EventHandler/* const& */rhs )
  {
  m_Handler.remove(rhs);
  return *this;
  }
  private:
  std::list<EventHandler> m_Handler;
  std::map<EventHandler,ReturnValue> m_ReturnValue;
  };
  event重载了+=, -=操作符,使用上完全跟C#原生的event操作基本一样。event实际上就是一个多播delegate,如果不需要多播,直接使用delegate就可以了。
  目前的实现还有一些小问题:
  1.目前event不支持void返回类型,其实只要写一个event<void, TArg>偏特化就可,因为我是在VC6下写的代码,而VC6不支持模板偏特化
  2. 由于C++语言对模板的一些限制,不得以在头文件中定义了一些静态成员变量,所以当在多个.cpp文件中包含这个头文件时会有链接错误,VC下可以使用_declspec(selectany)解决这个问题,别的编译器我就不知道了。

责任编辑:小草

文章搜索:
 相关文章
热点资讯
资讯快报
热门课程培训