c++回调函数 callback

1Callback方式
Callback
的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件:

?

  1. typedef?void?(__stdcall?*DownloadCallback)(const?char*?pURL,?bool?bOK);??
  2. void?DownloadFile(const?char*?pURL,?DownloadCallback?callback)??
  3. {??
  4. ????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;??
  5. ????callback(pURL,?true);??
  6. }??
  7. void?__stdcall?OnDownloadFinished(const?char*?pURL,?bool?bOK)??
  8. {??
  9. ????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;??
  10. }??

(2)Sink方式
Sink
的本质是你按照对方要求实现一个C++接口,然后把你实现的接口设置给对方,对方需要触发事件时调用该接口, COM中连接点就是居于这种方式。上面下载文件的需求,如果用Sink实现,代码如下:

  1. class?IDownloadSink??
  2. {??
  3. public:??
  4. ????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)?=?0;??
  5. };??
  6. ?? ?
  7. ?? ?
  8. class?CMyDownloader??
  9. {??
  10. public:??
  11. ????CMyDownloader(IDownloadSink*?pSink)??
  12. ????????:m_pSink(pSink)??
  13. ????{??
  14. ????}??
  15. ?? ?
  16. ????void?DownloadFile(const?char*?pURL)??
  17. ????{??
  18. ????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;??
  19. ????????if(m_pSink?!=?NULL)??
  20. ????????{??
  21. ????????????m_pSink->OnDownloadFinished(pURL,?true);??
  22. ????????}??
  23. ????}??
  24. ?? ?
  25. private:??
  26. ????IDownloadSink*?m_pSink;??
  27. };??
  28. ?? ?
  29. class?CMyFile:?public?IDownloadSink??
  30. {??
  31. public:??
  32. ????void?download()??
  33. ????{??
  34. ????????CMyDownloader?downloader(this);??
  35. ????????downloader.DownloadFile("www.baidu.com");??
  36. ????}??
  37. ?? ?
  38. ????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)??
  39. ????{??
  40. ????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;??
  41. ????}??
  42. };??

? ?

(3)Delegate方式
??? Delegate
的本质是设置成员函数指针给对方,然后让对方在需要触发事件时调用。C#中用Delegate的方式实现Event,让C++程序员很是羡慕,C++中因为语言本身的关系,要实现Delegate还是很麻烦的。上面的例子我们用Delegate的方式实现如下:

  1. class?CDownloadDelegateBase??
  2. {??
  3. public:??
  4. ????virtual?void?Fire(const?char*?pURL,?bool?bOK)?=?0;??
  5. };??
  6. ?? ?
  7. template<typename?O,?typename?T>??
  8. class?CDownloadDelegate:?public?CDownloadDelegateBase??
  9. {??
  10. ????typedef?void?(T::*Fun)(const?char*,?bool);??
  11. public:??
  12. ????CDownloadDelegate(O*?pObj?=?NULL,?Fun?pFun?=?NULL)??
  13. ????????:m_pFun(pFun),?m_pObj(pObj)??
  14. ????{??
  15. ????}??
  16. ????? ?
  17. ????virtual?void?Fire(const?char*?pURL,?bool?bOK)??
  18. ????{??
  19. ????????if(m_pFun?!=?NULL??
  20. ????????????&&?m_pObj?!=?NULL)??
  21. ????????{??
  22. ????????????(m_pObj->*m_pFun)(pURL,?bOK);??
  23. ????????}??
  24. ????}??
  25. ?? ?
  26. private:??
  27. ????Fun?m_pFun;??
  28. ????O*?m_pObj;??
  29. };??
  30. ?? ?
  31. template<typename?O,?typename?T>??
  32. CDownloadDelegate<O,T>*?MakeDelegate(O*?pObject,?void?(T::*pFun)(const?char*?pURL,?bool))??
  33. {??
  34. ????return?new?CDownloadDelegate<O,?T>(pObject,?pFun);??
  35. }??
  36. ?? ?
  37. class?CDownloadEvent??
  38. {??
  39. public:??
  40. ????~CDownloadEvent()??
  41. ????{??
  42. ????????vector<CDownloadDelegateBase*>::iterator?itr?=?m_arDelegates.begin();??
  43. ????????while?(itr?!=?m_arDelegates.end())??
  44. ????????{??
  45. ????????????delete?*itr;??
  46. ????????????++itr;??
  47. ????????}??
  48. ????????m_arDelegates.clear();??
  49. ????}??
  50. ?? ?
  51. ????void?operator?+=?(CDownloadDelegateBase*?p)??
  52. ????{??
  53. ????????m_arDelegates.push_back(p);??
  54. ????}??
  55. ?? ?
  56. ????void?operator?-=?(CDownloadDelegateBase*?p)??
  57. ????{??
  58. ????????ITR?itr?=?remove(m_arDelegates.begin(),?m_arDelegates.end(),?p);??
  59. ?? ?
  60. ????????ITR?itrTemp?=?itr;??
  61. ????????while?(itrTemp?!=?m_arDelegates.end())??
  62. ????????{??
  63. ????????????delete?*itr;??
  64. ????????????++itr;??
  65. ????????}??
  66. ????????m_arDelegates.erase(itr,?m_arDelegates.end());??
  67. ????}??
  68. ?? ?
  69. ????void?operator()(const?char*?pURL,?bool?bOK)??
  70. ????{??
  71. ????????ITR?itrTemp?=?m_arDelegates.begin();??
  72. ????????while?(itrTemp?!=?m_arDelegates.end())??
  73. ????????{??
  74. ????????????(*itrTemp)->Fire(pURL,?bOK);??
  75. ????????????++itrTemp;??
  76. ????????}??
  77. ????}??
  78. ?? ?
  79. private:??
  80. ????vector<CDownloadDelegateBase*>?m_arDelegates;??
  81. ????typedef?vector<CDownloadDelegateBase*>::iterator?ITR;??
  82. };??
  83. ?? ?
  84. ?? ?
  85. class?CMyDownloaderEx??
  86. {??
  87. public:??
  88. ????void?DownloadFile(const?char*?pURL)??
  89. ????{??
  90. ????????cout?<<?"downloading:?"?<<?pURL?<<?""?<<?endl;??
  91. ????????downloadEvent(pURL,?true);??
  92. ????}??
  93. ?? ?
  94. ????CDownloadEvent?downloadEvent;??
  95. };??
  96. ?? ?
  97. class?CMyFileEx??
  98. {??
  99. public:??
  100. ????void?download()??
  101. ????{??
  102. ????????CMyDownloaderEx?downloader;??
  103. ????????downloader.downloadEvent?+=?MakeDelegate(this,?&CMyFileEx::OnDownloadFinished);??
  104. ????????downloader.DownloadFile("www.baidu.com");??
  105. ????}??
  106. ?? ?
  107. ????virtual?void?OnDownloadFinished(const?char*?pURL,?bool?bOK)??
  108. ????{??
  109. ????????cout?<<?"OnDownloadFinished,?URL:"?<<?pURL?<<?"????status:"?<<?bOK?<<?endl;??
  110. ????}??
  111. };??

? ?

??? 可以看到Delegate的方式代码量比上面其他2种方式大多了,并且我们上面是固定参数数量和类型的实现方式,如果要实现可变参数,要更加麻烦的多。可变参数的方式可以参考这2种实现:

Yet Another C#-style Delegate Class in Standard C++
Member Function Pointers and the Fastest Possible C++ Delegates

? ?

我们可以用下面的代码测试我们上面的实现:

[cpp]?view plaincopy

  1. int?_tmain(int?argc,?_TCHAR*?argv[])??
  2. {??
  3. ?? ?
  4. ????DownloadFile("www.baidu.com",?OnDownloadFinished);??
  5. ?? ?
  6. ????CMyFile?f1;??
  7. ????f1.download();??
  8. ?? ?
  9. ????CMyFileEx?ff;??
  10. ????ff.download();??
  11. ?? ?
  12. ????system("pause");??
  13. ?? ?
  14. ????return?0;??
  15. }??


最后简单比较下上面3种实现回调的方法:


第一种Callback的方法是面向过程的,使用简单而且灵活,正如C语言本身。
第二种Sink的方法是面向对象的,在C++里使用较多, 可以在一个Sink里封装一组回调接口,适用于一系列比较固定的回调事件。
第三种Delegate的方法也是面向对象的,和Sink封装一组接口不同,Delegate的封装是以函数为单位,粒度比Sink更小更灵活。

你更倾向于用哪种方式来实现回调?

郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。