在工作中不断的遇到和听到回调函数,但自己对回调函数一直并未尝试使用,感觉理解也不够清晰。下面摘了一些其他看起来通俗易懂的说明,先记下来,备忘。

    

     1、基础知识

所谓回调,就是模块A要通过模块B的某个函数b()完成一定的功能,但是函数b()自己无法实现全部功能,需要反过头来调用模块A中的某个函数a()来完成,这个a()就是回调函数。如下图

lua带参数的回调 回调参数是什么意思_lua带参数的回调

①约定接口规范。在模块B必须约定接口规范,也就是定义回调函数a()的函数原型

这里回调函数原型的定义最好遵循typedef void (*SCT_XXX)(LPVOID lp, const CBParamStruct& cbNode); SCT_XXX是回调函数名称,lp是回调上下文,CBParamStruct是回调参数,一般由于要回调的参数不止一个,所以定义一个结构体比较方便。

②回调函数的注册。为了让模块B知道自己将要使用的回调函数,必须有一个函数或语句来注册回调函数

注册回调函数的定义遵循void RCF_XXX(SCT_XXX pfn, LPVOID lp); RCF_XXX是注册函数名,pfn是回调函数名称(是指针),lp是回调上下文。一般在A模块初始化完B模块后调用,将A模块中定义的回调函数地址赋值给pfn,lp赋值为this。 

③在模块A中要做的事情:

首先将回调函数声明成静态的,static void  CF_XXX(LPVOID lp, const CBParamStruct& cbNode); 函数的参数必须与B模块中回调函数原型的参数保持一致。

初始化B模块时,调用注册函数将模块A中声明的回调函数CF_XXX的地址传给pfn,即pfn=CF_XXX;(函数名称CF_XXX其实是个指针,指向回调函数的地址) 。

 

 2、举例

回调函数的yi个应用场景:网络编程。 在网络编程中,为了体现模块化,一般把通讯和数据处理划分开来,即通讯模块负责协议定义、数据收发,而数据处理模块只负责对收发的数据进行解析和打包,假如通讯模块开启了一个线程在持续地接收数据,这个时候问题来了,它通过什么手段把数据交到数据处理模块手中呢?每次收到数据,拿到数据处理模块的指针完成相关操作,这样有犯了两个类指针互相指的错误,也破坏了两个模块的独立性。使用回调函数这些问题都迎刃而解了,下面给出部分伪代码:

通讯模块

typedef void (*DataReceiveCBFunc)(ReceiveParam & recvParam);  // 回调函数原型定义
 
// 开始接收,数据处理模块调用,相对于注册回调函数
static BOOL StartReceive(DataReceiveCBFunc pfnData, LPVOID lpContext,……);
// 接收数据的线程,一收到数据就通知回调
static UINT TH_Receive(LPVOID lp);

数据处理模块

// 开始接收数据,开启监听线程,调用上面的StartReceive函数
int StartReceiveInfo(int nListenPort, std::string strLocalIP);
// 数据接收回调函数,被CUdpEx::TH_Receive()回调
static void RecvInfoCallback(ReceiveParam &recvParam);

 

3、关于常见的几个函数构成的回调函数

下面来说说回调函数的几个要点:

[ 被调用的函数A ]    可以认为是最后核心的被执行的函数;这个就是回调函数。

[ 对被调用函数进行调用的函数B ]   可以认为是绑定这个被调函数A的地方,让它以一种什么样的形式来使用(例如在循环体中执行N次,对别的变量赋值等等);

[ 触发主动调用函数的函数C ]   真正开始触发各个函数被调用的地方。

在上面的这三个函数A、B、C中,函数A和C通常是在一个应用层级来写的,而函数B则可能在较低的层级实现。

 

第一块所实现的一些应用逻辑的功能函数,就是函数A或者函数C,具体看是哪一个被传入函数B的,哪一个是主动调B的,被当做参数传入给B的,就是回调函数A,主动调B的,是真正触发各个函数被调用的函数C。

#include 
 #include 
  
 //简单点,让我们命名的方式简单点
 /* 定义一种输出为int型,输入参数为两个int型的这一类的函数,并给它一个统一的类别名字,callBackfunName*/
 typedef int(*funName)(int x1, int x2);
  
  
 /* 下述 add() 和 mix() 是两个符合funName这种形式的函数,通常它们处在应用层的下一层,即库函数或者外部接口等*/
 int add(int x, int y)          //函数A,回调函数
 {
     printf("add() is called.\n");
     return (x+y);
 }
  
 int mix(int x, int y)       
 {
     printf("mix() is called.\n");
     return (x*y);
 }
  
  
 void call_by_funName(funName f,int x)//函数B,调用调用函数A的函数  callBackfunName此时就被当做int char等一样的形式来用!
 {
     int ret = -100;
     printf("call_by_funName is begain.\n");
     ret = f(x,x);
     printf("call_by_funName is finished.\n");
     printf("[ %d ]\n",ret);
 }
  
 int main()//函数C,调用B的函数,首先引起各个调用的地方
 {
     callBackfunName f1 = mix;
     callBackfunName f2 = add;
     call_by_funName(f1,10);
     call_by_funName(f2,20);
     system("pause");
     return 0;
 }

 

四、回调函数的通俗解释:

        你到一个商店买东西,刚好你要的东西没有货,于是你在店员那里留下了你的电话,过了几天店里有货了,店员就打了你的电话,然后你接到电话后就到店里去取了货。在这个例子里,你的电话号码就叫回调函数,你把电话留给店员就叫登记回调函数,店里后来有货了叫做触发了回调关联的事件,店员给你打电话叫做调用回调函数,你到店里去取货叫做响应回调事件。