C#中的事件机制可以很方便的实现设计模式中的Observer模式,C#提供了delegate 和 event 来实现这种机制,实际上只要delagate就可以实现event效果,event语法完全没必要,因为delegate是多播的。本文提供了一个C++版本的实现,与C#原生的事件机制相比,只有一点不同,我实现的delegate是单播的(为了避免delegate 和 event 功能重复的问题)。 3COME文档编辑
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)解决这个问题,别的编译器我就不知道了。
3COME考试频道为您精心整理,希望对您有所帮助,更多信息在http://www.reader8.com/exam/