`
chinamming
  • 浏览: 140766 次
  • 性别: Icon_minigender_1
  • 来自: 北京
文章分类
社区版块
存档分类
最新评论

C++中实现回调机制的几种方式

 
阅读更多
(1)Callback方式
Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。

比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件:
typedefvoid(__stdcall*DownloadCallback)(constchar*pURL,boolbOK);
voidDownloadFile(constchar*pURL,DownloadCallbackcallback)
{
cout<<"downloading:"<<pURL<<""<<endl;
callback(pURL,true);
}
void__stdcallOnDownloadFinished(constchar*pURL,boolbOK)
{
cout<<"OnDownloadFinished,URL:"<<pURL<<"status:"<<bOK<<endl;
}

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

上面下载文件的需求,如果用Sink实现,代码如下:
classIDownloadSink
{
public:
virtualvoidOnDownloadFinished(constchar*pURL,boolbOK)=0;
};
classCMyDownloader
{
public:
CMyDownloader(IDownloadSink*pSink)
:m_pSink(pSink)
{
}

voidDownloadFile(constchar*pURL)
{
cout<<"downloading:"<<pURL<<""<<endl;
if(m_pSink!=NULL)
{
m_pSink->OnDownloadFinished(pURL,true);
}
}

private:
IDownloadSink*m_pSink;
};

classCMyFile:publicIDownloadSink
{
public:
voiddownload()
{
CMyDownloaderdownloader(this);
downloader.DownloadFile("www.baidu.com");
}

virtualvoidOnDownloadFinished(constchar*pURL,boolbOK)
{
cout<<"OnDownloadFinished,URL:"<<pURL<<"status:"<<bOK<<endl;
}
};

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

template<typenameO,typenameT>
classCDownloadDelegate:publicCDownloadDelegateBase
{
typedefvoid(T::*Fun)(constchar*,bool);
public:
CDownloadDelegate(O*pObj=NULL,FunpFun=NULL)
:m_pFun(pFun),m_pObj(pObj)
{
}

virtualvoidFire(constchar*pURL,boolbOK)
{
if(m_pFun!=NULL
&&m_pObj!=NULL)
{
(m_pObj->*m_pFun)(pURL,bOK);
}
}

private:
Funm_pFun;
O*m_pObj;
};

template<typenameO,typenameT>
CDownloadDelegate<O,T>*MakeDelegate(O*pObject,void(T::*pFun)(constchar*pURL,bool))
{
returnnewCDownloadDelegate<O,T>(pObject,pFun);
}

classCDownloadEvent
{
public:
~CDownloadEvent()
{
vector<CDownloadDelegateBase*>::iteratoritr=m_arDelegates.begin();
while(itr!=m_arDelegates.end())
{
delete*itr;
++itr;
}
m_arDelegates.clear();
}

voidoperator+=(CDownloadDelegateBase*p)
{
m_arDelegates.push_back(p);
}

voidoperator-=(CDownloadDelegateBase*p)
{
ITRitr=remove(m_arDelegates.begin(),m_arDelegates.end(),p);

ITRitrTemp=itr;
while(itrTemp!=m_arDelegates.end())
{
delete*itr;
++itr;
}
m_arDelegates.erase(itr,m_arDelegates.end());
}

voidoperator()(constchar*pURL,boolbOK)
{
ITRitrTemp=m_arDelegates.begin();
while(itrTemp!=m_arDelegates.end())
{
(*itrTemp)->Fire(pURL,bOK);
++itrTemp;
}
}

private:
vector<CDownloadDelegateBase*>m_arDelegates;
typedefvector<CDownloadDelegateBase*>::iteratorITR;
};


classCMyDownloaderEx
{
public:
voidDownloadFile(constchar*pURL)
{
cout<<"downloading:"<<pURL<<""<<endl;
downloadEvent(pURL,true);
}

CDownloadEventdownloadEvent;
};

classCMyFileEx
{
public:
voiddownload()
{
CMyDownloaderExdownloader;
downloader.downloadEvent+=MakeDelegate(this,&CMyFileEx::OnDownloadFinished);
downloader.DownloadFile("www.baidu.com");
}

virtualvoidOnDownloadFinished(constchar*pURL,boolbOK)
{
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(intargc,_TCHAR*argv[])
{

DownloadFile("www.baidu.com",OnDownloadFinished);

CMyFilef1;
f1.download();

CMyFileExff;
ff.download();

system("pause");

return0;
}


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

你更倾向于用哪种方式来实现回调?
分享到:
评论

相关推荐

    Visual C++2010开发权威指南(共三部分).part1.rar

    5.4.9 在列表控件中实现工作区 205 5.4.10 处理列表控件中的通知消息 206 5.4.11 更改列表控件样式 206 5.4.12 虚拟列表控件 207 5.4.13 列表控件的消息映射 209 5.4.14 列表控件的风格选项及表头设置 210 5.4.15 ...

    Visual C++2010开发权威指南.part02

    5.4.9 在列表控件中实现工作区 205 5.4.10 处理列表控件中的通知 5.4.10 消息 206 5.4.11 更改列表控件样式 206 5.4.12 虚拟列表控件 207 5.4.13 列表控件的消息映射 209 5.4.14 列表控件的风格选项及表头 5.4.14 ...

    C/C++笔试题(附答案,华为面试题系列)

    中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 ...

    ActiveQtServer

    基于QtActiveServer的开发笔记,其中主要是针对web应用与C++的通信,内容上主要是记录了几种回调机制的实现。

    最新名企标准通用C++面试题,

    15、内存的分配方式的分配方式有几种? 答: 1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。 2. 在栈上创建。在执行函数时,函数内局部变量的存储...

    vc++ 开发实例源码包

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 如题。 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了...

    vc++ 应用源码包_6

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...

    VC学习大纲 VC学习讲义

    如何为程序中添加选项菜单和选项设置对话框,如何使用标准颜色对话框,窗口重绘原理,在选项对话框中实现预览功能。实现选项对话框和窗口类中的数据交换。 第十三课: 如何让CDC上输出的文字、图形具有保持功能,元...

    VC++2010权威开发指南+源代码

     5.4.9 在列表控件中实现工作区 205  5.4.10 处理列表控件中的通知消息 206  5.4.11 更改列表控件样式 206  5.4.12 虚拟列表控件 207  5.4.13 列表控件的消息映射 209  5.4.14 列表控件的风格选项及表头设置 ...

    VC++2010权威开发指南+源代码.part2

     5.4.9 在列表控件中实现工作区 205  5.4.10 处理列表控件中的通知消息 206  5.4.11 更改列表控件样式 206  5.4.12 虚拟列表控件 207  5.4.13 列表控件的消息映射 209  5.4.14 列表控件的风格选项及表头设置 ...

    asp.net知识库

    Asp.Net2.0无刷新客户端回调 体验.net 2.0 的优雅(1) -- 异步WebService调用 ASP.NET 2.0页面框架的几点新功能 ASP.NET 2.0 中收集的小功能点 asp.net2.0中的webpart使用小记 2.0问题、错误解决办法 ASP.NET 2.0...

    传智播客扫地僧视频讲义源码

    08_C动态库升级成框架案例_方法1动态库中直接添加回调函数_传智扫地僧 09_C动态库升级成框架案例_方法2把回调函数缓存到动态库_编写 10_C动态库升级成框架案例_方法2把回调函数混存到动态库_测试 11_C++基础课程day...

    java 面试题 总结

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...

    超级有影响力霸气的Java面试题大全文档

    assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...

    vc++ 应用源码包_1

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...

    vc++ 应用源码包_5

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...

    vc++ 应用源码包_2

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...

    vc++ 应用源码包_3

    DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...

    antlr4权威指南

    ANTLR生成的语法分析器能够自动建立名为语法分析树(parse tree)的视图,其他程序可以遍历此树,并在所需处理的结构处触发回调函数。在先前的ANTLR 3中,用户需要补充语法来创建树。除了自动建立树结构之外,ANTLR ...

Global site tag (gtag.js) - Google Analytics