實現回調的三種方式(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); 太不方便了, 不知道大家有沒有好的方法。