程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 在C++中實現事件(委托)

在C++中實現事件(委托)

編輯:C++入門知識

 實現回調的三種方式(C風格的回調函數, Sink方式和Delegate方式)。在面向對象開發中,delegate的方式是最靈活和方便的,因此很早就有人用復雜的模板去模擬(有興趣的話可以看這裡和這裡),總之是實現起來很復雜。但是現在借助C++11的function和bind, 我們可以很方便的去實現。下面是我自己的一種實現方式:   namespace Common {     typedef void* cookie_type;       template<typename TR, typename T1, typename T2>     class CEvent     {     public:         typedef TR return_type;         typedef T1 first_type;         typedef T2 second_type;           typedef std::function<return_type (first_type, second_type)> handler_type;           ~CEvent()         {             Clear();         }           return_type operator()(first_type p1, second_type p2)         {             return_type ret = return_type();             size_t size = _handlers.size();             for(size_t i=0; i<size; ++i)             {                 ret = _handlers[i]->operator()(p1, p2);             }             return ret;         }           cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)         {             CEventHandler*p = new(nothrow)  CEventHandler(h);             if(p != nullptr) _handlers.push_back(p);             return (cookie_type)p;         }           template<typename class_type, typename class_fun>         cookie_type AddHandler(class_type* pThis, class_fun f)         {             CEventHandler* p = new(nothrow) CEventHandler(pThis, f);             if(p != nullptr) _handlers.push_back(p);             return (cookie_type)p;         }           void RemoveHandler(cookie_type cookie)         {             CEventHandler* p = (CEventHandler*)cookie;               auto itr = std::find(_handlers.begin(), _handlers.end(), p);             if(itr != _handlers.end())             {                 _handlers.erase(itr);                 delete p;             }             else             {                 assert(false);             }         }           void Clear()         {             if(!_handlers.empty())             {                 int n = _handlers.size();                 std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)                 {                      assert(p != nullptr);                     delete p;                 });                 _handlers.clear();                     }         }       private:         class CEventHandler          {         public:             CEventHandler(handler_type h)             {                 _handler = h;                 assert(_handler != nullptr);             }               template<typename class_type, typename class_fun>             CEventHandler(class_type* pThis, class_fun object_function)             {                 using namespace std::placeholders;                 _handler = std::bind(object_function, pThis, _1, _2);                 assert(_handler != nullptr);             }               return_type operator()(first_type p1, second_type p2)             {                 return_type ret = return_type();                 assert(_handler != nullptr);                 if(_handler != nullptr) ret = _handler(p1, p2);                 return ret;             }               handler_type _handler;         };         private:         std::vector<CEventHandler*> _handlers;     }; }   大概實現思想是我們通過一個內置的CEventHandler 類來封裝處理函數,我們可以通過AddHandler來添加事件處理函數,添加時會返回一個Cookie,我們可以通過該Cookie來RemoveHandler, 下面是測試代碼: #include "stdafx.h" #include <iostream> #include "event1.h"   using namespace std;   class CObjectX  {   };   class CClickEventArgs: public CObjectX {   };     class CButton: public CObjectX { public:     void FireClick()     {         CClickEventArgs args;         OnClicked(this, args);     }       Common::CEvent<int, CObjectX*, CClickEventArgs&> OnClicked; };     class CMyClass  { public:     int OnBtuttonClicked(CObjectX* pButton, CClickEventArgs& args)     {         cout << "CMyClass: Receive button clicked event" << endl;         return 1;     } };   int OnBtuttonClicked_C_fun(CObjectX* pButton, CClickEventArgs& args) {     cout << "C Style Function: Receive button clicked event" << endl;     return 1; }     class CMyFunObj { public:     int operator()(CObjectX* pButton, CClickEventArgs& args)     {         cout << "Functor: Receive button clicked event" << endl;         return 1;     } };   int _tmain(int argc, _TCHAR* argv[]) {     using namespace std::placeholders;       CButton btn;       CMyClass obj;     Common::cookie_type c1 = btn.OnClicked.AddHandler(&obj, &CMyClass::OnBtuttonClicked);       Common::cookie_type c2 = btn.OnClicked.AddHandler(OnBtuttonClicked_C_fun);       CMyFunObj functor;     Common::cookie_type c3 = btn.OnClicked.AddHandler(functor);       btn.FireClick();         btn.OnClicked.RemoveHandler(c2);       std::cout << endl;         btn.FireClick();       system("pause");       return 0; }   以下是測試結果:    可以看到, 我們在普通C函數, 類成員函數和仿函數(functor)中都測試通過。   另外對於事件函數返回值為void的情況,會編譯出錯,我們需要偏特化一下:     template< typename T1, typename T2>     class CEvent<void, T1, T2>     {     public:         typedef void return_type;         typedef T1 first_type;         typedef T2 second_type;           typedef std::function<return_type (first_type, second_type)> handler_type;           ~CEvent()         {             Clear();         }           return_type operator()(first_type p1, second_type p2)         {             size_t size = _handlers.size();             for(size_t i=0; i<size; ++i)             {                 _handlers[i]->operator()(p1, p2);             }         }           cookie_type AddHandler(std::function<return_type (first_type, second_type)> h)         {             CEventHandler*p = new(nothrow)  CEventHandler(h);             if(p != nullptr) _handlers.push_back(p);             return (cookie_type)p;         }           template<typename class_type, typename class_fun>         cookie_type AddHandler(class_type* pThis, class_fun f)         {             CEventHandler* p = new(nothrow) CEventHandler(pThis, f);             if(p != nullptr) _handlers.push_back(p);             return (cookie_type)p;         }           void RemoveHandler(cookie_type cookie)         {             CEventHandler* p = (CEventHandler*)cookie;               auto itr = std::find(_handlers.begin(), _handlers.end(), p);             if(itr != _handlers.end())             {                 _handlers.erase(itr);                 delete p;             }             else             {                 assert(false);             }         }           void Clear()         {             if(!_handlers.empty())             {                 int n = _handlers.size();                 std::for_each(_handlers.begin(), _handlers.end(), [](CEventHandler* p)                 {                      assert(p != nullptr);                     delete p;                 });                 _handlers.clear();                     }         }       private:         class CEventHandler          {         public:             CEventHandler(handler_type h)             {                 _handler = h;                 assert(_handler != nullptr);             }               template<typename class_type, typename class_fun>             CEventHandler(class_type* pThis, class_fun object_function)             {                 using namespace std::placeholders;                 _handler = std::bind(object_function, pThis, _1, _2);                 assert(_handler != nullptr);             }               return_type operator()(first_type p1, second_type p2)             {                 assert(_handler != nullptr);                 if(_handler != nullptr) _handler(p1, p2);             }               handler_type _handler;         };         private:         std::vector<CEventHandler*> _handlers;     };   最後談一下在寫這個代碼中遇到的問題: (1)不知道你能不能發現下面代碼的問題, 我在寫代碼時就栽在這裡了:      vector<int*>  v;     int* p1 = new int(1);     v.push_back(p1);     int* p2 = new int(2);     v.push_back(p2);       //嘗試刪除所有值為p1的項     //由該代碼想到=>v.erase(std::remove(v.begin(), v.end(), p1), v.end());     auto itr = remove(v.begin(), v.end(), p1);     for_each(itr, v.end(), [](int* p){delete p;});     v.erase(itr, v.end());   (2)我們想把cookei_type放到類裡面去, 類似這樣: 1     template<typename TR, typename T1, typename T2> 2     class CEvent 3     { 4     public: 5         typedef TR return_type; 6         typedef T1 first_type; 7         typedef T2 second_type; 8         typedef void* cookie_type;   可發現要這樣使用: Common::CEvent<int, CObjectX*, CClickEventArgs&>::cookie_type c1 = btn.OnClicked.AddHandler(&obj, &CMyClass::OnBtuttonClicked); 太不方便了, 不知道大家有沒有好的方法。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved