C++中实现回调机制的几种方式
生活随笔
收集整理的這篇文章主要介紹了
C++中实现回调机制的几种方式
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
(1)Callback方式
Callback的本質是設置一個函數指針進去,然后在需要需要觸發某個事件時調用該方法, 比如Windows的窗口消息處理函數就是這種類型。
比如下面的示例代碼,我們在Download完成時需要觸發一個通知外面的事件:
typedef?void?(__stdcall?*DownloadCallback)(const?char*?pURL,?bool?bOK);
void?DownloadFile(const?char*?pURL,?DownloadCallback?callback)
{
????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????callback(pURL,?true);
}
void?__stdcall?OnDownloadFinished(const?char*?pURL,?bool?bOK)
{
????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
}
(2)Sink方式
Sink的本質是你按照對方要求實現一個C++接口,然后把你實現的接口設置給對方,對方需要觸發事件時調用該接口, COM中連接點就是居于這種方式。
上面下載文件的需求,如果用Sink實現,代碼如下:
class?IDownloadSink
{
public:
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)?=?0;
};
class?CMyDownloader
{
public:
????CMyDownloader(IDownloadSink*?pSink)
????????:m_pSink(pSink)
????{
????}
????void?DownloadFile(const?char*?pURL)
????{
????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????????if(m_pSink?!=?NULL)
????????{
????????????m_pSink->OnDownloadFinished(pURL,?true);
????????}
????}
private:
????IDownloadSink*?m_pSink;
};
class?CMyFile:?public?IDownloadSink
{
public:
????void?download()
????{
????????CMyDownloader?downloader(this);
????????downloader.DownloadFile("www.baidu.com");
????}
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)
????{
????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
????}
};
(3)Delegate方式
Delegate的本質是設置成員函數指針給對方,然后讓對方在需要觸發事件時調用。
C#中用Delegate的方式實現Event,讓C++程序員很是羨慕,C++中因為語言本身的關系,要實現Delegate還是很麻煩的。
上面的例子我們用Delegate的方式實現如下:?
class?CDownloadDelegateBase
{
public:
????virtual?void?Fire(const?char*?pURL,?bool?bOK)?=?0;
};
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;
};
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);
}
class?CDownloadEvent
{
public:
????~CDownloadEvent()
????{
????????vector<CDownloadDelegateBase*>::iterator?itr?=?m_arDelegates.begin();
????????while?(itr?!=?m_arDelegates.end())
????????{
????????????delete?*itr;
????????????++itr;
????????}
????????m_arDelegates.clear();
????}
????void?operator?+=?(CDownloadDelegateBase*?p)
????{
????????m_arDelegates.push_back(p);
????}
????void?operator?-=?(CDownloadDelegateBase*?p)
????{
????????ITR?itr?=?remove(m_arDelegates.begin(),?m_arDelegates.end(),?p);
????????ITR?itrTemp?=?itr;
????????while?(itrTemp?!=?m_arDelegates.end())
????????{
????????????delete?*itr;
????????????++itr;
????????}
????????m_arDelegates.erase(itr,?m_arDelegates.end());
????}
????void?operator()(const?char*?pURL,?bool?bOK)
????{
????????ITR?itrTemp?=?m_arDelegates.begin();
????????while?(itrTemp?!=?m_arDelegates.end())
????????{
????????????(*itrTemp)->Fire(pURL,?bOK);
????????????++itrTemp;
????????}
????}
private:
????vector<CDownloadDelegateBase*>?m_arDelegates;
????typedef?vector<CDownloadDelegateBase*>::iterator?ITR;
};
class?CMyDownloaderEx
{
public:
????void?DownloadFile(const?char*?pURL)
????{
????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????????downloadEvent(pURL,?true);
????}
????CDownloadEvent?downloadEvent;
};
class?CMyFileEx
{
public:
????void?download()
????{
????????CMyDownloaderEx?downloader;
????????downloader.downloadEvent?+=?MakeDelegate(this,?&CMyFileEx::OnDownloadFinished);
????????downloader.DownloadFile("www.baidu.com");
????}
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)
????{
????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
????}
};
可以看到Delegate的方式代碼量比上面其他2種方式大多了,并且我們上面是固定參數數量和類型的實現方式,如果要實現可變參數,要更加麻煩的多。
可變參數的方式可以參考這2種實現:
Yet Another C#-style Delegate Class in Standard C++
Member Function Pointers and the Fastest Possible C++ Delegates
我們可以用下面的代碼測試我們上面的實現:
int?_tmain(int?argc,?_TCHAR*?argv[])
{
????DownloadFile("www.baidu.com",?OnDownloadFinished);
????CMyFile?f1;
????f1.download();
????CMyFileEx?ff;
????ff.download();
????system("pause");
????return?0;
}
最后簡單比較下上面3種實現回調的方法:
第一種Callback的方法是面向過程的,使用簡單而且靈活,正如C語言本身。
第二種Sink的方法是面向對象的,在C++里使用較多, 可以在一個Sink里封裝一組回調接口,適用于一系列比較固定的回調事件。
第三種Delegate的方法也是面向對象的,和Sink封裝一組接口不同,Delegate的封裝是以函數為單位,粒度比Sink更小更靈活。?
你更傾向于用哪種方式來實現回調?
Callback的本質是設置一個函數指針進去,然后在需要需要觸發某個事件時調用該方法, 比如Windows的窗口消息處理函數就是這種類型。
比如下面的示例代碼,我們在Download完成時需要觸發一個通知外面的事件:
typedef?void?(__stdcall?*DownloadCallback)(const?char*?pURL,?bool?bOK);
void?DownloadFile(const?char*?pURL,?DownloadCallback?callback)
{
????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????callback(pURL,?true);
}
void?__stdcall?OnDownloadFinished(const?char*?pURL,?bool?bOK)
{
????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
}
(2)Sink方式
Sink的本質是你按照對方要求實現一個C++接口,然后把你實現的接口設置給對方,對方需要觸發事件時調用該接口, COM中連接點就是居于這種方式。
上面下載文件的需求,如果用Sink實現,代碼如下:
class?IDownloadSink
{
public:
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)?=?0;
};
class?CMyDownloader
{
public:
????CMyDownloader(IDownloadSink*?pSink)
????????:m_pSink(pSink)
????{
????}
????void?DownloadFile(const?char*?pURL)
????{
????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????????if(m_pSink?!=?NULL)
????????{
????????????m_pSink->OnDownloadFinished(pURL,?true);
????????}
????}
private:
????IDownloadSink*?m_pSink;
};
class?CMyFile:?public?IDownloadSink
{
public:
????void?download()
????{
????????CMyDownloader?downloader(this);
????????downloader.DownloadFile("www.baidu.com");
????}
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)
????{
????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
????}
};
(3)Delegate方式
Delegate的本質是設置成員函數指針給對方,然后讓對方在需要觸發事件時調用。
C#中用Delegate的方式實現Event,讓C++程序員很是羨慕,C++中因為語言本身的關系,要實現Delegate還是很麻煩的。
上面的例子我們用Delegate的方式實現如下:?
class?CDownloadDelegateBase
{
public:
????virtual?void?Fire(const?char*?pURL,?bool?bOK)?=?0;
};
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;
};
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);
}
class?CDownloadEvent
{
public:
????~CDownloadEvent()
????{
????????vector<CDownloadDelegateBase*>::iterator?itr?=?m_arDelegates.begin();
????????while?(itr?!=?m_arDelegates.end())
????????{
????????????delete?*itr;
????????????++itr;
????????}
????????m_arDelegates.clear();
????}
????void?operator?+=?(CDownloadDelegateBase*?p)
????{
????????m_arDelegates.push_back(p);
????}
????void?operator?-=?(CDownloadDelegateBase*?p)
????{
????????ITR?itr?=?remove(m_arDelegates.begin(),?m_arDelegates.end(),?p);
????????ITR?itrTemp?=?itr;
????????while?(itrTemp?!=?m_arDelegates.end())
????????{
????????????delete?*itr;
????????????++itr;
????????}
????????m_arDelegates.erase(itr,?m_arDelegates.end());
????}
????void?operator()(const?char*?pURL,?bool?bOK)
????{
????????ITR?itrTemp?=?m_arDelegates.begin();
????????while?(itrTemp?!=?m_arDelegates.end())
????????{
????????????(*itrTemp)->Fire(pURL,?bOK);
????????????++itrTemp;
????????}
????}
private:
????vector<CDownloadDelegateBase*>?m_arDelegates;
????typedef?vector<CDownloadDelegateBase*>::iterator?ITR;
};
class?CMyDownloaderEx
{
public:
????void?DownloadFile(const?char*?pURL)
????{
????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;
????????downloadEvent(pURL,?true);
????}
????CDownloadEvent?downloadEvent;
};
class?CMyFileEx
{
public:
????void?download()
????{
????????CMyDownloaderEx?downloader;
????????downloader.downloadEvent?+=?MakeDelegate(this,?&CMyFileEx::OnDownloadFinished);
????????downloader.DownloadFile("www.baidu.com");
????}
????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)
????{
????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;
????}
};
可以看到Delegate的方式代碼量比上面其他2種方式大多了,并且我們上面是固定參數數量和類型的實現方式,如果要實現可變參數,要更加麻煩的多。
可變參數的方式可以參考這2種實現:
Yet Another C#-style Delegate Class in Standard C++
Member Function Pointers and the Fastest Possible C++ Delegates
我們可以用下面的代碼測試我們上面的實現:
int?_tmain(int?argc,?_TCHAR*?argv[])
{
????DownloadFile("www.baidu.com",?OnDownloadFinished);
????CMyFile?f1;
????f1.download();
????CMyFileEx?ff;
????ff.download();
????system("pause");
????return?0;
}
最后簡單比較下上面3種實現回調的方法:
第一種Callback的方法是面向過程的,使用簡單而且靈活,正如C語言本身。
第二種Sink的方法是面向對象的,在C++里使用較多, 可以在一個Sink里封裝一組回調接口,適用于一系列比較固定的回調事件。
第三種Delegate的方法也是面向對象的,和Sink封裝一組接口不同,Delegate的封裝是以函數為單位,粒度比Sink更小更靈活。?
你更傾向于用哪種方式來實現回調?
總結
以上是生活随笔為你收集整理的C++中实现回调机制的几种方式的全部內容,希望文章能夠幫你解決所遇到的問題。