(1)Callback方式
Callback的本质是设置一个函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。
比如下面的示例代码,我们在Download完成时需要触发一个通知外面的事件:
typedef
void(__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
{
typedef
void(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更小更灵活。
你更倾向于用哪种方式来实现回调?
相关推荐
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 ...
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 ...
中实现的线程,其不依赖于操作系统核心,应用进程利用线程库提供创建、同步、调度 和管理线程的函数来控制用户线程。这种线程甚至在象 DOS 这样的操作系统中也可实现 ,但线程的调度需要用户程序完成,这有些类似 ...
基于QtActiveServer的开发笔记,其中主要是针对web应用与C++的通信,内容上主要是记录了几种回调机制的实现。
15、内存的分配方式的分配方式有几种? 答: 1. 从静态存储区域分配。内存在程序编译的时候就已经分配好,这块内存在程序的整个运行期间都存在。例如全局变量。 2. 在栈上创建。在执行函数时,函数内局部变量的存储...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 如题。 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
如何为程序中添加选项菜单和选项设置对话框,如何使用标准颜色对话框,窗口重绘原理,在选项对话框中实现预览功能。实现选项对话框和窗口类中的数据交换。 第十三课: 如何让CDC上输出的文字、图形具有保持功能,元...
5.4.9 在列表控件中实现工作区 205 5.4.10 处理列表控件中的通知消息 206 5.4.11 更改列表控件样式 206 5.4.12 虚拟列表控件 207 5.4.13 列表控件的消息映射 209 5.4.14 列表控件的风格选项及表头设置 ...
5.4.9 在列表控件中实现工作区 205 5.4.10 处理列表控件中的通知消息 206 5.4.11 更改列表控件样式 206 5.4.12 虚拟列表控件 207 5.4.13 列表控件的消息映射 209 5.4.14 列表控件的风格选项及表头设置 ...
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...
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...
assertion(断言)在软件开发中是一种常用的调试方式,很多开发语言中都支持这种机制。在实现中,assertion就是在程序中的一条语句,它对一个boolean表达式进行检查,一个正确程序必须保证这个boolean表达式的值为...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
DirectUI移植到MFC中实现。 MFCHtml 调用脚本 MFC使用COM加载WMI服务,另类获取系统服务详细 大家都知道,现在流行的检测硬件软件视乎很神秘,我们要获得各种信息好像比较难.但大多数这种软件或多或少的使用了WMI,如果...
ANTLR生成的语法分析器能够自动建立名为语法分析树(parse tree)的视图,其他程序可以遍历此树,并在所需处理的结构处触发回调函数。在先前的ANTLR 3中,用户需要补充语法来创建树。除了自动建立树结构之外,ANTLR ...