C语言函数指针之回调函数及其应用场景.docx
1什么是回调函数?首先什么是“回调''呢?我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调。回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。2为什么要用回调函数?因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。主函数Call库函数intCalIbackOUK回调函数/TODOreturn0;intmain()/<主函数TODO1.ibrary(Callback);HK库函数通过函数指针进行回调/TODOreturn0;回调似乎只是函数间的调用,和普通函数调用没啥区别。但仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀。3怎么使用回调函数?intCallback_l(inta)/<回调函数1printf(,Hello,thisisCallback_l:a=%d”,a);return0;intCallback_2(intb)/V回调函数2printf(',Hello,thisisCallback_2:b=%d",b);return0;intCallback_3(intc)/<回调函数3printf("Hello,thisisCallback_3:c=%d",c);return0;intHandle(intx,int(*Callback)(int)/<注意这里用到的函数指针定义Callback(x);)intmain()Handle(4,Callback_l);Handle(5,Callback_2);Handle(6,Callback_3);return0;如上述代码:可以看到,Handleo函数里面的参数是一个指针,在main。函数里调用HandIe()函数的时候,给它传入了函数CalIbaCk()/Callback_2()/CanbaCk_3()的函数名,这时候的函数名就是对应函数的指针,山就是说,回调函数其实底是函数指针的一种用法。4回调函数实例(很有用)一个GPRS模块联网的小项目,使用过的同学大概知道2G、4G、NB等模块要想实现无线联网功能都需要经历模块上电初始化、注册网络、查询网络信息质量、连接服务器等步骤,这里的的例子就是,利用一个状态机函数(根据不同状态依次调用不同实现方法的函数),通过回调函数的方式依次调用不同的函数,实现模块联网功能,如下:/*工作状态处理*typedefstruct(uint8_tmStatus;Uint8(*FUntion)(VOid);函数指针的形式M26_WorkStatus-TypeDef;/M26的工作状态集合调用函数*>M26工作状态集合函数M26_WorkStatus_TypeDefM26_WorkStatus_Tab=GPRS_NETWORK_CLOSE,M26_PWRKEY_Off,模块关机GPRS-NETWORK-OPEN,M26_PWRKEY_On,模块开机GPRS_NETWORK_Start,M26_WorkJnit,/管脚初始化,/连接调度中心), 等待调度中心回复),/连接前置机,/等待前置机回复GPRS-NETWORK-CONF,M26_NET_Config,/AT指令配置GPRS_NETWORK_LINK_CTC,M26_LINK_CTCGPRS_NETWORK_WAIT_CTC,M26_WAIT_CTCGPRS_NETWORK_LINK_FEM,M26_LINK_FEMGPRS_NETWORK_WAIT_FEM,M26_WA1T_FEMGPRS_NETWORK_COMM,M26.COMM),正常工作GPRS_NETWORK_WAIT_Sig,M26_WAIT_Sig,等待信号回复GPRS_NETWORK_GetSignal,M26_GetSignal,获取信号值GPRS_NETWORK_RESTART,M26_RESET,模块重启1*>M26模块工作状态机,依次调用里面的12个函数uint8_tM26_WorkStatus_Call(uint8_tStart)uint8_ti=O;for(i=0;i<12;i+÷)if(Start=M26_WorkStatus_Tab|i.mStatus)returnM26_WorkStatus_Tabi.Funtion();)return0;所以,如巢有人想做个NB模块联网项目,可以COPy上面的框架,只需要修改回调函数内部的具体实现,或者增加、减少回调函数,就可以很简洁快速的实现模块联网。C+编程中回调函数的应用场景回调函数在C+编程中有广泛的应用场景,特别是在处理异步操作、事件处理和定制化行为方面。以下是一些常见的应用场景:回调函数是指在某个特定事件发生或异步任务完成时被调用的函数。在C+中,回调函数可以是全局函数、静态成员函数、普通成员函数或Lambda表达式等。通常,回调函数通过函数指针、函数对象或函数模板来传递。回调对象是一个包含回调函数的对象,通常用于在多个回调函数之间共享状态或参数。通过使用回调对象,可以更灵活地管理回调函数的调用顺序和参数传递。1 .异步编程:在多线程、异步任务或事件驱动编程中,回调函数用于处理异步操作的完成或错误。通过在异步操作开始时注册回调函数,在操作完成时触发回调,可以有效地处理异步结果;异步操作函数,接受回调函数作为参数voidasyncperation(void(*callback)(int)/模拟异步操作intresult=performAsyncOperationO;异步操作完成后调用回调函数callback(result);2 .事件处理:在GUl编程和图形库中,回调函数用于响应用户交互或系统事件。例如,按钮点击、鼠标移动或键盘按键等事件可以通过注册回调函数来处理;3 /注册按钮点击事件回调button.onClick(callbackFunction);4 .定时器:回调函数可以用于处理定时器到期时的操作。定时器库通常允许开发者注册回调函数,在特定时间间隔或延迟之后执行相应的操作。/注册定时器回调函数timer.registerCallback(callbackFunction);5 .自定义行为:在某些情况下,库或框架提供了可以自定义行为的接口,通过注册回调函数可以实现定制化的功能。这种情况下,回调函数允许开发者在特定事件发生时注入自己的逻辑。/注册自定义行为的回调函数framework.setCustomBehaviorCallback(customBehaviorFunction);6 .插件系统:在应用程序中实现插件系统时,回调函数常用于定义插件接口。应用程序在运行时可以加载插件,并将插件提供的回调函数注册到适当的事件。7 /插件接口定义classPluginpublic:virtualvoidonEvent()=0;8 /注册插件回调函数Plugin*plugin=loadPlugin();plugin->onEvent();这些场景只是回调函数在C+中的一部分应用示例,实际上,回调函数是一种灵活的编程模式,可以根据具体需求在各种情境中使用。回调函数使得程序具有更好的扩展性和可维护性,同时提高了代码的模块化和复用性。c+÷回调函数的使用java的实现方式java的回调函数可能都不陌生,使用接口interface的方式,在接口中定义回调函数。函数参数可以是interfance。调用函数的时候,实现这个interface的函数即可。简单示例:publicinterfaceCallBackpublicvoidexecute();)publicvoidtest(CallBackcallBack)System.out.println(callback");callBack.execute();test(newCallBack()Overridepublicvoidexecute()System.out.println(callbackimplement););c+的实现c+是通过函数指针(c的语法)和std:function(c+ll里面的语法)来实现的。c+÷回调函数的使用场景有下面几种:回调函数是普通函数回调函数的使用场景一般是,一个函数中最后产生一个结果,该函数不再去管这个结果后续的使用,而使用回调函数进行处理。可以先定义好回调函数的函数指针,一般格式:返回值(*指针名)(参数列表)typedefvoid(*CaptureCallback)(string);voidcapturePic(CaptureCallbackcallback)stringt="apicn;callback(t);voidrenderPic(stringt)print(t);)capturePic(renderPic);上面就是一个简单的例子,在捕获图片的函数里面使用渲染图片的回调函数。回调函数是成员函数在c+面向对象里面,回调函数是成员函数的情况更常见,这样的好处是,一个类A的一个函数生成一个结果之后,可以调用另一个类B的成员函数。而不必类A拥有B的实例。尤其是在B中有了A的实例的情况下,A中如果再包含B的实例会出现循环引用的问题,这也是可以解决的,但是这种耦合还是容易让逻辑变得混乱。typedefstd:function<void(string)>CaptureCallback;classCaptureControllerpublic:CaptureCalIbackcallback;CaptureController(CaptureCallbackCallback):CailbaCk(CalIbaCk);voidcapturePic(CaptureCallbackcallback)stringt=,apic'callback(t);classUI开始捕捉图像voidStartCaptureOCaptureControIlerc(std:bind(&UI:renderPic,this,_i);c.capturePic();演染图片作为回调函数voidrenderPic(stringt)print(t);)main.cppUIui;ui.StartCaptureO;上面的例子就很好的说明了为什么需要回调函数,以及回调函数是如何使用的。在Ul的类中已经引用了CaPtUreCO