C#中的事件機(jī)制可以很方便的實(shí)現(xiàn)設(shè)計(jì)模式中的Observer模式,C#提供了delegate 和 event 來(lái)實(shí)現(xiàn)這種機(jī)制,實(shí)際上只要delagate就可以實(shí)現(xiàn)event效果,event語(yǔ)法完全沒必要,因?yàn)閐elegate是多播的。本文提供了一個(gè)C++版本的實(shí)現(xiàn),與C#原生的事件機(jī)制相比,只有一點(diǎn)不同,我實(shí)現(xiàn)的delegate是單播的(為了避免delegate 和 event 功能重復(fù)的問(wèn)題)。
C# delegate 本質(zhì)上是一個(gè)函數(shù)的面向?qū)ο蟮姆庋b, 在C++語(yǔ)言中函數(shù)分好多種,包括 全局函數(shù),成員函數(shù),函數(shù)對(duì)象(即functor,雖然不是函數(shù),但因?yàn)樾袨橄窈瘮?shù),所以歸為函數(shù)一類),考試.大提示因此在C++里實(shí)現(xiàn)delegate的關(guān)鍵就是封裝上述3類函數(shù)的不同,對(duì)外提供一致的接口,先來(lái)看一下delegate的實(shí)現(xiàn)。
template
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::Bind(pCallback);
m_pInvoker = Invoker::Invoke;
}
// for object member methods
template
Delegate(TObject* pObject, TReturn (TObject::*pCallback)(TArgument))
:m_pInvoker(NULL)
{
MemberInvoker::Bind(pObject,pCallback);
m_pInvoker = MemberInvoker::Invoke;
}
// for functor methods
template
Delegate(TFunctor* pCallback)
:m_pInvoker(NULL)
{
FunctorInvoker::Bind(pCallback);
m_pInvoker = FunctorInvoker::Invoke;
}
TReturn operator() (TArgument args)
{
return m_pInvoker(args);
}
//implementations
private:
InvokerType m_pInvoker;
};
delegate 本身就是一個(gè)函數(shù)對(duì)象,針對(duì)C++里3類函數(shù)提供了3個(gè)構(gòu)造函數(shù),每個(gè)構(gòu)造函數(shù)的實(shí)現(xiàn)分別用到了一個(gè)輔助類別,分別是Invoker(用于全局函數(shù)或者類的靜態(tài)函數(shù)),MemberInvoker(用于類成員函數(shù)),F(xiàn)unctorInvoker(用于functor) ,這三個(gè)輔助類別主要用于在編譯時(shí)保存函數(shù)類型,對(duì)象類型等信息。實(shí)現(xiàn)如下:
template
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
typename Invoker::Method Invoker::m_pCallback = NULL;
template
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
TObject* MemberInvoker::m_pObject = NULL;
template
typename MemberInvoker::MemberMethod MemberInvoker::m_pCallback = NULL;
template
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
typename FunctorInvoker::FunctorMethod FunctorInvoker::m_pCallback = NULL;
至此,一個(gè)完整的delegate實(shí)現(xiàn)完畢,我們可以用它來(lái)實(shí)現(xiàn)event 了。
event實(shí)現(xiàn)如下,為了實(shí)現(xiàn)多播用std::list保存多個(gè)deledate:
template< class TReturn, class TArgument>
class Event
{
public:
typedef TReturn ReturnValue;
typedef TArgument EventArgs;
typedef Delegate 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::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 m_Handler;
std::map m_ReturnValue;
};
event重載了+=, -=操作符,使用上完全跟C#原生的event操作基本一樣。event實(shí)際上就是一個(gè)多播delegate,如果不需要多播,直接使用delegate就可以了。
目前的實(shí)現(xiàn)還有一些小問(wèn)題:
1.目前event不支持void返回類型,其實(shí)只要寫一個(gè)event偏特化就可,因?yàn)槲沂窃赩C6下寫的代碼,而VC6不支持模板偏特化
2. 由于C++語(yǔ)言對(duì)模板的一些限制,不得以在頭文件中定義了一些靜態(tài)成員變量,所以當(dāng)在多個(gè).cpp文件中包含這個(gè)頭文件時(shí)會(huì)有鏈接錯(cuò)誤,VC下可以使用_declspec(selectany)解決這個(gè)問(wèn)題,別的編譯器我就不知道了。
C# delegate 本質(zhì)上是一個(gè)函數(shù)的面向?qū)ο蟮姆庋b, 在C++語(yǔ)言中函數(shù)分好多種,包括 全局函數(shù),成員函數(shù),函數(shù)對(duì)象(即functor,雖然不是函數(shù),但因?yàn)樾袨橄窈瘮?shù),所以歸為函數(shù)一類),考試.大提示因此在C++里實(shí)現(xiàn)delegate的關(guān)鍵就是封裝上述3類函數(shù)的不同,對(duì)外提供一致的接口,先來(lái)看一下delegate的實(shí)現(xiàn)。
template
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
m_pInvoker = Invoker
}
// for object member methods
template
Delegate(TObject* pObject, TReturn (TObject::*pCallback)(TArgument))
:m_pInvoker(NULL)
{
MemberInvoker
m_pInvoker = MemberInvoker
}
// for functor methods
template
Delegate(TFunctor* pCallback)
:m_pInvoker(NULL)
{
FunctorInvoker
m_pInvoker = FunctorInvoker
}
TReturn operator() (TArgument args)
{
return m_pInvoker(args);
}
//implementations
private:
InvokerType m_pInvoker;
};
delegate 本身就是一個(gè)函數(shù)對(duì)象,針對(duì)C++里3類函數(shù)提供了3個(gè)構(gòu)造函數(shù),每個(gè)構(gòu)造函數(shù)的實(shí)現(xiàn)分別用到了一個(gè)輔助類別,分別是Invoker(用于全局函數(shù)或者類的靜態(tài)函數(shù)),MemberInvoker(用于類成員函數(shù)),F(xiàn)unctorInvoker(用于functor) ,這三個(gè)輔助類別主要用于在編譯時(shí)保存函數(shù)類型,對(duì)象類型等信息。實(shí)現(xiàn)如下:
template
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
typename Invoker
template
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
TObject* MemberInvoker
template
typename MemberInvoker
template
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
typename FunctorInvoker
至此,一個(gè)完整的delegate實(shí)現(xiàn)完畢,我們可以用它來(lái)實(shí)現(xiàn)event 了。
event實(shí)現(xiàn)如下,為了實(shí)現(xiàn)多播用std::list保存多個(gè)deledate:
template< class TReturn, class TArgument>
class Event
{
public:
typedef TReturn ReturnValue;
typedef TArgument EventArgs;
typedef Delegate
public:
Event(){}
virtual ~Event(){}
public:
ReturnValue GetReturnValue(const EventHandler& rhs )
{
return m_ReturnValue[rhs];
}
ReturnValue operator() (EventArgs/* const& */rhs)
{
ReturnValue RetValue;
for (std::list
{
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
std::map
};
event重載了+=, -=操作符,使用上完全跟C#原生的event操作基本一樣。event實(shí)際上就是一個(gè)多播delegate,如果不需要多播,直接使用delegate就可以了。
目前的實(shí)現(xiàn)還有一些小問(wèn)題:
1.目前event不支持void返回類型,其實(shí)只要寫一個(gè)event
2. 由于C++語(yǔ)言對(duì)模板的一些限制,不得以在頭文件中定義了一些靜態(tài)成員變量,所以當(dāng)在多個(gè).cpp文件中包含這個(gè)頭文件時(shí)會(huì)有鏈接錯(cuò)誤,VC下可以使用_declspec(selectany)解決這個(gè)問(wèn)題,別的編譯器我就不知道了。