回调函数
1、什么是回调函数。
软件模块之间通信、调用总有一定的机制,从调用方式上可以分为:同步调用、异步调用和回调。同步调用的一种阻塞调用,调用方必须等待被调用方执行完毕后,才能继续执行,所以造成在执行效率的低下。异步调用是一种类似消息或者事件机制,不过它的调用方向相反,接口的服务在收到某种信息后或者发生某种事件后,会主动通知客户方(即调用客户的接口)。回调函数则是一种双向调用模式,也就是,被调用方在接口被调用是,也会调用对方的接口。(异步是单向的)。回调和异步的之间联系比较密切,通常我们有异步调用来注册回调函数,通过异步来实现信息的获取。
回调函数是一种函数或者过程,不过它是由调用方自己实现,供被调用者使用的特殊函数。在windows平台的消息机制可以看做回调函数的特例,通过系统提供的接口注册消息处理函数,从而实现接收和处理函数。
2、过程语言中的回调函数
2.1函数指针
在C语言中应用回调函数,一般是想调用者的参数列表中,传入回调函数的函数指针。比如:
void Func(char* S);//函数原型 void (pFunc)(char* s);//函数指针
下面讲一个调用例子:
typedef void(* pfun)(char *);//一位这个函数指针中指向的函数的返回值类型,以及参数类型个数,只要合适,在调用函数中都能传入。
void GetCallBack(pfun callback) {dosomething;}
用户在调用上面的函数,需要自己实现一个pfunc类型的回调函数。比如:void Func(char *s);
然后可以调用了:GetCallBack(Func);
如果赋值了不同的值给该参数,那么调用者就将调用不同地址的函数。赋值可以在运行时,这样使你能实现动态绑定。
2.2参数调用机制
到目前为止,我们只讨论了函数指针及回调而没有去注意ANSI C/C++的编译器规范。
许多编译器有几种调用规范。如在Visual C++中,可以在函数类型前加_cdecl,_stdcall或者_pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall
调用规范。调用规范影响编译器产生的给定函数名,参数传递的顺序(从右到左或从左到右),堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈,CPU寄存器等)。在回调函数中一定注意调用机制,如果调用机制出现问题,将无法有效进行调用。
将调用规范看成回调函数中重要的一部分很重要,不能用不兼容的调用规范,将地址赋值给函数指针。
__stdcall int Func(int);//被调用函数为__stdcall void caller(__cdcel int(*ptr)(int));//调用函数以函数指针为参数,但调用规范为_cdcel. _cdcel int (*ptr)(int) = Func;//报错.调用规范不一致。
3、面向对象的回调函数
static int __stdcall VodFileInfoCallback_xxx(const ST_VodFileInfo vodFileInfo,const ISOC_DWORDPTR dwCookie,const ISOC_STR strQueryGUID);//厂家sdk中回调函数的调用规范以及返回类型,参数类型等。
为了可以调用,在调用函数的头文件里定义自己的回调函数:
static int __stdcall VodFileInfoCallback_owner(const ST_VodFileInfo vodFileInfo,const ISOC_DWORDPTR dwCookie,const ISOC_STR strQueryGUID);//函数名称可以不同,但是调用规范,返回值类型,参数类型个数必须一致。
调用函数:(调用函数当然是从厂家中提取出来的)<pre name="code" class="html">int GetVodFileList( LONG_PTR UserID,const char * strDeviceList,ST_VodQueryFilter stQueryFilter,VodFileInfoCallback_xxx pCallBack, DWORD_PTR dwCookie );在此函数实现时(cpp)文件,函数调用可以为:
this-> GetVodFileList( UserID, strDeviceList,stQueryFilter,VodFileInfoCallback_owner , this );//其中this代表<pre name="code" class="html">VodFileInfoCallback_owner在当前文件中进行实现,加入不是,下面讲讲。同时
int __stdcall VodFileInfoCallback_owner(const ST_VodFileInfo vodFileInfo,const ISOC_DWORDPTR dwCookie,const ISOC_STR strQueryGUID) { do something; CClassa *ptr = (CClass*)dwcookie;//定义这样的话,便可以通过ptr调用类中的成员函数以及成员变量。 }
加入回调函数:VodFileInfoCallback_owner与调用者不在同一层的话,就不能用this。而是用传下来的。
int CallBack(VodFileInfoCallback_owner callback,void * puser) { callback(vodfileifo,puser,strqueryguid); }
这里重点是讲puser指针的问题,其他当然要赋值,只有这样才能调用class1中回调函数。