C++實例:用C++模擬C#事件機制

字號:

C#中的事件機制可以很方便的實現(xiàn)設(shè)計模式中的Observer模式,C#提供了delegate 和 event 來實現(xiàn)這種機制,實際上只要delagate就可以實現(xiàn)event效果,event語法完全沒必要,因為delegate是多播的。本文提供了一個C++版本的實現(xiàn),與C#原生的事件機制相比,只有一點不同,我實現(xiàn)的delegate是單播的(為了避免delegate 和 event 功能重復(fù)的問題)。
    C# delegate 本質(zhì)上是一個函數(shù)的面向?qū)ο蟮姆庋b, 在C++語言中函數(shù)分好多種,包括 全局函數(shù),成員函數(shù),函數(shù)對象(即functor,雖然不是函數(shù),但因為行為像函數(shù),所以歸為函數(shù)一類),考試.大提示因此在C++里實現(xiàn)delegate的關(guān)鍵就是封裝上述3類函數(shù)的不同,對外提供一致的接口,先來看一下delegate的實現(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 本身就是一個函數(shù)對象,針對C++里3類函數(shù)提供了3個構(gòu)造函數(shù),每個構(gòu)造函數(shù)的實現(xiàn)分別用到了一個輔助類別,分別是Invoker(用于全局函數(shù)或者類的靜態(tài)函數(shù)),MemberInvoker(用于類成員函數(shù)),F(xiàn)unctorInvoker(用于functor) ,這三個輔助類別主要用于在編譯時保存函數(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;
    至此,一個完整的delegate實現(xiàn)完畢,我們可以用它來實現(xiàn)event 了。
    event實現(xiàn)如下,為了實現(xiàn)多播用std::list保存多個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實際上就是一個多播delegate,如果不需要多播,直接使用delegate就可以了。
    目前的實現(xiàn)還有一些小問題:
    1.目前event不支持void返回類型,其實只要寫一個event偏特化就可,因為我是在VC6下寫的代碼,而VC6不支持模板偏特化
    2. 由于C++語言對模板的一些限制,不得以在頭文件中定義了一些靜態(tài)成員變量,所以當在多個.cpp文件中包含這個頭文件時會有鏈接錯誤,VC下可以使用_declspec(selectany)解決這個問題,別的編譯器我就不知道了。