在C++中實現(xiàn)回調(diào)機制的幾種方式一文中,我們提到了實現(xiàn)回調(diào)的三種方式(C風(fēng)格的回調(diào)函數(shù), Sink方式和Delegate方式)。在面向?qū)ο箝_發(fā)中,delegate的方式是最靈活和方便的,因此很早就有人用復(fù)雜的模板去模擬(有興趣的話可以看這里和這里),總之是實現(xiàn)起來很復(fù)雜。但是現(xiàn)在借助C++11的function和bind, 我們可以很方便的去實現(xiàn)。下面是我自己的一種實現(xiàn)方式: 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;
};
}
大概實現(xiàn)思想是我們通過一個內(nèi)置的CEventHandler 類來封裝處理函數(shù),我們可以通過AddHandler來添加事件處理函數(shù),添加時會返回一個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;
}
以下是測試結(jié)果:

可以看到, 我們在普通C函數(shù), 類成員函數(shù)和仿函數(shù)(functor)中都測試通過。另外對于事件函數(shù)返回值為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)不知道你能不能發(fā)現(xiàn)下面代碼的問題, 我在寫代碼時就栽在這里了: 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());
//正確代碼:
v.erase(remove_if(v.begin(), v.end(), [](int*p)->bool {if(p==p1) {delete p; return true;} else return false}), 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;
可發(fā)現(xiàn)要這樣使用:Common::CEvent<int, CObjectX*, CClickEventArgs&>::cookie_type c1 = btn.OnClicked.AddHandler(&obj, &CMyClass::OnBtuttonClicked);
太不方便了, 不知道大家有沒有好的方法。注:上面的代碼還沒有經(jīng)過真正商業(yè)使用,如果有問題歡迎指出。
posted on 2013-01-31 14:16
Richard Wei 閱讀(10359)
評論(8) 編輯 收藏 引用 所屬分類:
C++