C++回调机制的几种实现方式
Callback
Callback的本質(zhì)是設(shè)置一個(gè)函數(shù)指針進(jìn)去,然后在需要觸發(fā)某個(gè)事件時(shí)調(diào)用該方法, 比如Windows的窗口消息處理函數(shù)就是這種類型。
比如下面的示例代碼,我們?cè)贒ownload完成時(shí)需要觸發(fā)一個(gè)通知外面的事件:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
Sink
Sink的本質(zhì)是你按照對(duì)方要求實(shí)現(xiàn)一個(gè)C++接口,然后把你實(shí)現(xiàn)的接口設(shè)置給對(duì)方,對(duì)方需要觸發(fā)事件時(shí)調(diào)用該接口, COM中連接點(diǎn)就是居于這種方式。還是以上面的Download為例(你調(diào)用對(duì)方的下載類實(shí)現(xiàn)下載功能):
/*對(duì)方要求的接口*/ class IDownloadSink { public:virtual void OnDownloadFinished(const char* pURL, bool bOK) = 0; }; /*對(duì)方的實(shí)現(xiàn)*/ class CMyDownloader { public:CMyDownloader(IDownloadSink* pSink) : m_pSink(pSink) { }void DownloadFile(const char* pURL){……if(m_pSink != NULL)m_pSink->OnDownloadFinished(pURL, true);}private:IDownloadSink* m_pSink; };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 15
Delegate
Delegate的本質(zhì)是設(shè)置成員函數(shù)指針給對(duì)方,然后讓對(duì)方在需要觸發(fā)事件時(shí)調(diào)用。C#中用Delegate的方式實(shí)現(xiàn)Event,讓C++程序員很是羨慕,C++中因?yàn)檎Z(yǔ)言本身的關(guān)系,要實(shí)現(xiàn)Delegate還是很麻煩的。上面的例子我們用Delegate的方式實(shí)現(xiàn)如下:
class CDownloadDelegateBase { public:virtual void Fire(const char* pURL, bool bOK) = 0; };/*模板類,實(shí)現(xiàn)代理函數(shù)的調(diào)用*/ template<typename O, typename T> class CDownloadDelegate: public CDownloadDelegateBase {typedef void (T::*Fun)(const char*, bool); public:CDownloadDelegate(O* pObj = NULL, Fun pFun = NULL):m_pFun(pFun), m_pObj(pObj){ } virtual void Fire(const char* pURL, bool bOK){if(m_pFun != NULL && m_pObj != NULL){(m_pObj->*m_pFun)(pURL, bOK);}} private:Fun m_pFun;O* m_pObj; };/*模板函數(shù),創(chuàng)建代理*/ template<typename O, typename T> CDownloadDelegate<O,T>* MakeDelegate(O* pObject, void (T::*pFun)(const char* pURL, bool)) {return new CDownloadDelegate<O, T>(pObject, pFun); }/*代理函數(shù)管理*/ typedef vector<CDownloadDelegateBase*> CDownloadDelegates; class CDownloadEvent { public:~CDownloadEvent(){CDownloadDelegates::iterator it = m_arDelegates.begin();while (it != m_arDelegates.end()){delete *it;++it;}m_arDelegates.clear();}void operator += (CDownloadDelegateBase* p){m_arDelegates.push_back(p);}void operator -= (CDownloadDelegateBase* p){CDownloadDelegates::iterator it = remove(m_arDelegates.begin(), m_arDelegates.end(), p);while (it != m_arDelegates.end()){delete *it;++it;}m_arDelegates.erase(it, m_arDelegates.end());}void operator()(const char* pURL, bool bOK){CDownloadDelegates::iterator it = m_arDelegates.begin();while (it != m_arDelegates.end()){(*it)->Fire(pURL, bOK);++it;}} private:CDownloadDelegates m_arDelegates; };class CMyDownloaderEx { public:void DownloadFile(const char* pURL){……downloadEvent(pURL, true);}CDownloadEvent downloadEvent; };/*應(yīng)用*/ class CMyFileEx { public:void download(){CMyDownloaderEx downloader;/*添加代理函數(shù)*/downloader.downloadEvent += MakeDelegate(this, &CMyFileEx::OnDownloadFinished);downloader.DownloadFile("www.baidu.com");}virtual void OnDownloadFinished(const char* pURL, bool bOK){……} };- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 105
- 106
- 107
可以看到Delegate的方式代碼量比上面其他2種方式多多了,并且上面是固定參數(shù)數(shù)量和類型的實(shí)現(xiàn)方式,如果要實(shí)現(xiàn)可變參數(shù),要更加麻煩的多,具體可參考:
http://www.codeproject.com/Articles/11464/Yet-Another-C-style-Delegate-Class-in-Standard-C
http://www.codeproject.com/Articles/7150/Member-Function-Pointers-and-the-Fastest-Possible
std::function(since C++ 11, vs2010)
template< class R, class... Args > class function<R(Args...)>- 1
- 2
- 1
- 2
類模板std :: function是一個(gè)通用的多態(tài)函數(shù)包裝器。std :: function的實(shí)例可以存儲(chǔ),復(fù)制和調(diào)用任何可調(diào)用的目標(biāo):函數(shù)、lambda表達(dá)式、綁定表達(dá)式或其他函數(shù)對(duì)象。
#include <functional> #include <iostream>struct Foo {Foo(int num) : num_(num) {}void print_add(int i) const { std::cout << num_+i << '\n'; }int num_; };void print_num(int i) {std::cout << i << '\n'; }int main() {// store a free functionstd::function<void(int)> f1 = print_num;f1(-9);// store a lambdastd::function<void()> f2 = []() { print_num(123); };f2();// store the result of a call to std::bindstd::function<void()> f3 = std::bind(print_num, 12345);f3();// store a call to a member functionstd::function<void(const Foo&, int)> f4 = &Foo::print_add;Foo foo(13579);f4(foo, 1); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
std::mem_fn (since C++ 11, vs2010)
template< class R, class T > /*unspecified*/ mem_fn(R T::* pm); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...)); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) &); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const &); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile &); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile &); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) &&); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const &&); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) volatile &&); template< class R, class T, class... Args > /*unspecified*/ mem_fn(R (T::* pm)(Args...) const volatile &&);- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
函數(shù)模板std :: mem_fn生成指向成員函數(shù)的指針的包裝對(duì)象,它可以存儲(chǔ),復(fù)制和調(diào)用指向成員函數(shù)的指針。 在調(diào)用std :: mem_fn時(shí),可以使用對(duì)象的引用和指針(包括智能指針)。
/* Use mem_fn to store and execute a member function:*/ #include <functional> #include <iostream> struct Foo { void display_greeting() { std::cout << "Hello, world.\n"; } void display_number(int i) { std::cout << "number: " << i << '\n'; } }; int main() {Foo foo;auto func_greet = std::mem_fn(&Foo::display_greeting);func_greet(foo); auto func_display = std::mem_fn(&Foo::display_number);func_display(foo, 42); }- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
小結(jié)
1) Callback方法是面向過(guò)程的,使用簡(jiǎn)單而且靈活,正如C語(yǔ)言本身;
2) Sink方法是面向?qū)ο蟮?#xff0c;在C++里使用較多, 可以在一個(gè)Sink里封裝一組回調(diào)接口,適用于一系列比較固定的回調(diào)事件;
3) Delegate方法也是面向?qū)ο蟮?#xff0c;和Sink封裝一組接口不同,Delegate的封裝是以函數(shù)為單位,粒度比Sink更小更靈活;
4) std::function和std::bind組合使用也可以實(shí)現(xiàn)類似函數(shù)指針的功能,但卻卻比函數(shù)指針更加靈活,特別是函數(shù)指向類的非靜態(tài)成員函數(shù)時(shí)(本質(zhì)上講全局函數(shù)和靜態(tài)成員函數(shù)沒有區(qū)別,使用方法上除了靜態(tài)成員函數(shù)在引用時(shí)要在前面加域作用符classname::外,沒有其它任何區(qū)別;事實(shí)上全局函數(shù)也有可能放入命名空間或者使用全局域作用符,例如 namespace::function() 或::function,這樣不僅本質(zhì)上相同,形勢(shì)上也與靜態(tài)成員函數(shù)一致了)。在Effective C++中ITEM35建議使用該方法實(shí)現(xiàn)Strategy模式,實(shí)際上也可以用于代替callback 函數(shù)模仿C#中的event對(duì)象,而這里只能實(shí)現(xiàn)一個(gè)函數(shù),實(shí)際上如果function<>模板實(shí)現(xiàn)了operator+以后,再在 function對(duì)象中維護(hù)一個(gè)列表,便可以實(shí)現(xiàn)C#中的event特性,即Delegate。
總結(jié)
以上是生活随笔為你收集整理的C++回调机制的几种实现方式的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 程序员的本质
- 下一篇: s3c2440移植MQTT