首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

事件程式設計 (二):以仿函數與模板封裝通用事件機制

     編輯:關於VC++

回顧與問題

上篇 (事件程式設計 (一)) 實作 CPrimeCalculator 觸發 Progress/Done 事件,客戶端實作 IPrimeEvents 介面並註冊。但事件觸發程式碼冗長重複:每個事件需寫 NotifyFoo 遍歷客戶列表呼叫 OnFoo。且事件介面與事件源分離,難以重用。

目標:通用事件管理器

將事件機制封裝為單一可重用類別,使用仿函數 與模板 消除樣板程式碼。

仿函數

仿函數將函數轉為類別,取代回呼函數指標。配合 STL for_each 演算法:

class NotifyProgress : public unary_function<IPrimeEvents*, void>
{
    UINT m_nFound;
public:
    NotifyProgress(UINT n) : m_nFound(n) {}
    void operator()(IPrimeEvents* obj) { obj->OnProgress(m_nFound); }
};

繼承 unary_function 使其可配接 STL 配接器 (如 not1, bind2nd)。

事件介面內嵌仿函數

class IPrimeEvents
{
public:
    virtual ~IPrimeEvents() = 0;
    virtual void OnProgress(UINT nFound) {}
    virtual void OnDone() {}
    
    // 內嵌仿函數類別
    class Progress : public unary_function<IPrimeEvents*, void>
    {
        UINT m_nFound;
    public:
        Progress(UINT n) : m_nFound(n) {}
        void operator()(IPrimeEvents* obj) { obj->OnProgress(m_nFound); }
    };
    
    class Done : public unary_function<IPrimeEvents*, void>
    {
    public:
        void operator()(IPrimeEvents* obj) { obj->OnDone(); }
    };
};
inline IPrimeEvents::~IPrimeEvents() {}

通用事件管理器 CEventMgr

template <class I>
class CEventMgr
{
    list<I*> m_clients;
public:
    void Register(I* p) { m_clients.push_back(p); }
    void Unregister(I* p) { m_clients.remove(p); }
    
    template <class F>
    void Raise(F fn)
    {
        for_each(m_clients.begin(), m_clients.end(), fn);
    }
};

觸發事件僅需一行:

m_eventmgr.Raise(IPrimeEvents::Progress(nFound));

編譯器自動具現化 Raise<IPrimeEvents::Progress>,內部以 for_each 呼叫每個客戶的 operator(),最終觸發 OnProgress。

巨集簡化宣告

class IPrimeEvents
{
    DECLARE_EVENTS(IPrimeEvents);
public:
    DEFINE_EVENT1(IPrimeEvents, Progress, UINT);
    DEFINE_EVENT0(IPrimeEvents, Done);
};
IMPLEMENT_EVENTS(IPrimeEvents);

DEFINE_EVENT0/1/2 分別對應 0/1/2 參數事件,自動產生處理函數與仿函數。超過 2 參數建議封裝結構體傳指標。

效能考量

仿函數通常比函數指標更有效率:模板具現化時編譯器內聯展開,僅剩虛擬函數呼叫開銷。傳函數指標無法內聯。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved