回调函数(callback)是什么?(*****)
C++回调函数的理解与使用
** 10张图让你彻底理解回调函数:同步回调、异步回调
https://blog.csdn.net/ken2232/article/details/135777311
通俗理解“回调函数” https://blog.csdn.net/angciyu/article/details/80794273
回调函数(callback)是什么?一文理解回调函数(callback)
为什么HAL库有那么多的回调函数?如何理解? https://zhuanlan.zhihu.com/p/508936793
C 语言回调函数详解
https://www.runoob.com/w3cnote/c-callback-function.html
C++回调函数的理解与使用
https://www.cnblogs.com/zzw19940404/p/14152151.html
** C语言 回调函数 callback - C语言零基础入门教程 https://zhuanlan.zhihu.com/p/406020900
Callback Function in C Programming https://embetronicx.com/tutorials/p_language/c/callback-function-in-c-programming/
--------------------------------------------------------------------
关联参考:
STM32 回调函数 (***)https://blog.csdn.net/ken2232/article/details/135759275
回调函数(callback)是什么?(*****) https://blog.csdn.net/ken2232/article/details/132665409
-----------------------------------------------------------------
C语言中的回调函数(函数指针的应用,qsort的理解和用法)
https://blog.csdn.net/m0_46606290/article/details/119765528
1. 回调函数
回调函数通俗的讲就是通过函数指针(作为另一个函数形参的)调用的函数。
一般我们是把回调函数名(地址)作为参数,传递给另一个函数,在另一个函数中通过该回调函数指针,去调用它所指向的函数。
其实就是一个函数指针的应用而已。
但是,好处,便于实现:
- 针对接口编程
- 软件结构分层,使得可以分工完成
应用场景示例之一:
原始:1个老板 --> 管理5个工人。<< 没有问题的。
公司发展膨胀了:1个老板 --> 管理200个工人。<< 问题来了,此时,老板已经没有精力去做老板该做的事情了。
公司的组织架构需要变革:老板 --> 秘书 --> 管理200个工人。
公司发展再次膨胀了:老板 --> 秘书长 --> N个秘书 --> 管理2000个工人。
应用场景示例之二:
摘要:https://blog.csdn.net/ken2232/article/details/135777311
而之所以需要给第三方库指定回调函数,是因为“第三方库的编写者”并不清楚在某些特定节点,比如我们举的例子油条制作完成、接收到网络数据、文件读取完成等之后该做什么,这些只有“库的使用方”才知道,因此第三方库的编写者无法针对具体的实现来写代码,而只能对外提供一个回调函数,“库的使用方”来实现该函数,第三方库在特定的节点调用该回调函数就可以了。
callback (韦氏词典)
https://www.merriam-webster.com/dictionary/callback
1. <programming> A scheme used in event-driven programs where the program registers a subroutine (a "callback handler") (子例程)to handle a certain event. The program does not call the handler directly but when the event occurs, the run-time system calls the handler, usually passing it arguments to describe the event.
Callbacks in C
https://www.geeksforgeeks.org/callbacks-in-c/
A callback is any executable code that is passed as an argument to another code, which is expected to call back (execute) the argument at a given time. In simple language, If a reference of a function is passed to another function as an argument to call it, then it will be called a Callback function.
In C, a callback function is a function that is called through a function pointer.
Below is a simple example in C to illustrate the above definition to make it more clear.
// A simple C program to demonstrate callback
#include <stdio.h>
void A(){ // 回调函数 ?
printf("I am function A\n");
}
// callback function
void B(void (*ptr)()) // 中间函数 ?
{
(*ptr)(); // callback to A
}
int main()
{
void (*ptr)() = &A;
// calling function B and passing
// address of the function A as argument
B(ptr);
return 0;
}
Output
I am function A
-----------------------------------------------------------------
回调函数:本质上,只是一种“函数调用模型”,一种函数调用机制,一种函数调用的方法。回调函数其实就是“函数指针”的一种用法。
回调函数:一个独立的函数。
在函数的结构上,和普通函数没有什么不同。
但是,关键点是在“调用方法”上:在函数的调用方式上,在回调中,主函数把“回调函数”像(当作?)函数参数一样传入库函数;
“回调函数”这个概念的定义,很有问题的,导致人们在理解上的困难。
“回调函数”概念定义的实质:
当一个独立的函数
- 被作为主调函数的函数参数,
- 且被以“函数指针”的方式进行调用。
怎样对上述的函数调用模型,重新进行“概念定义”呢?
“ABC调用模式函数”?A=主函数,B=中间函数,C=被调用的目标函数。
“被以特别的方式进行调用的普通的独立函数”?概念也太长了。
想要重新创造一个容易被理解的新概念,来取代“回调函数”这个概念,太伤脑细胞了。
如果不创造新的概念,直接去理解一个糊里糊涂的旧概念,也太伤脑细胞了。
看到网络上,不少的人们,总是想着将一个稀里糊涂的概念,怎样去解释清楚?
却很少看到有人想着怎样废除掉一个稀里糊涂的概念,而采用一个容易理解的新概念来取代它。
------
所谓、callback 函数,就是:在“xx接口设计模式”中(或者说,在某种场景下),对所充当了某种角色的函数的称谓。
本质上,该函数就是一个普通的独立函数。
同一个“张三”(callback 函数):
在老老张的面前,叫:龟孙子。
在公司里,叫:张经理。
在 XX里,叫:ZZ。
-----------------------------------------------------------------
回调函数:回调 == 执行
https://www.bilibili.com/video/BV18X4y1M763?p=36
6:30
20230814_通俗易懂理解回调函数
https://www.bilibili.com/video/BV14a4y1y7Aw/?spm_id_from=333.337.search-card.all.click
回调函数并不是一种函数,而是一种函数调用机制。
回调函数实际上是一种编程方式,让你可以把自己的代码插入到别人的代码中,
以便在某种特定时间发生时被调用。这种方式使代码更加灵活,你不需要一直等待某个事件的发生。
而是在需要的时候才会被“回调”执行。
【简单认识】什么是回调函数,理解回调函数,弄懂回调函数,
https://www.bilibili.com/video/BV1s3411F7ip/?spm_id_from=333.337.search-card.all.click
当被作为参数来使用的函数,就是回调函数。
【一听就懂】回调函数!C语言中,如何通过函数指针实现回调函数
https://www.bilibili.com/video/BV1i84y1375F/?spm_id_from=333.337.search-card.all.click
函数也可以作为函数的参数来传递。
回调函数到底时怎么回事?
>> 至少需要 3种类型的函数 (? 实质上,就是一种函数之间进行相互调用关系的函数调用流程结构模型,一种函数调用机制 ?一种:函数调用模式)
主函数:相当于整个程序的引擎,调度各个函数程序执行
回调函数:一个独立的函数。
中间函数:一个介于主函数与回调函数之间的函数。登记回调函数,通知主函数,起到一个桥梁的作用。
单片机---函数指针和回调函数
https://www.bilibili.com/video/BV1Pe411H7C7/?spm_id_from=333.337.search-card.all.click
回调函数其实就是函数指针的一种用法。
-------
视频时间:11:50
主函数 -- 库函数 -- 回调函数1 /2 /3
回调函数与普通函数的一个关键的不同:在回调中,主函数把“回调函数”像(当作?)函数参数一样传入库函数;
这样依赖,只要我们概念传进库函数的参数,就可以实现不同的功能,这样很灵活。
当库函数很复杂、或者不可见的时候,利用回调函数就显得十分优秀。
-------
=====================================
C 语言回调函数详解
https://www.runoob.com/w3cnote/c-callback-function.html
1. 什么是回调函数?
回调函数,光听名字就比普通函数要高大上一些,那到底什么是回调函数呢?恕我读得书少,没有在那本书上看到关于回调函数的定义。我在百度上搜了一下,发现众说纷纭,有很大一部分都是使用类似这么一个场景来说明:A君去B君店里买东西,恰好缺货,A君留下号码给B君,有货时通知A君。感觉这个让人更容易想到的是异步操作,而不是回调。另外还有两句英文让我印象深刻:1) If you call me, I will call you back; 2) Don't call me, I will call you. 看起来好像很有道理,但是仔细一想,普通函数不也可以做到这两点吗?所以,我觉得这样的说法都不是很妥当,因为我觉得这些说法都没有把回调函数的特点表达出来,也就是都看不到和普通函数到底有什么差别。不过,百度百科的解析我觉得还算不错(虽然经常吐槽百度搜索...):回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
下面先说说我的看法。我们可以先在字面上先做个分解,对于"回调函数",中文其实可以理解为这么两种意思:1) 被回调的函数;2) 回头执行调用动作的函数。那这个回头调用又是什么鬼?
先来看看来自维基百科的对回调(Callback)的解析:In computer programming, a callback is any executable code that is passed as an argument to other code, which is expected to call back (execute) the argument at a given time. This execution may be immediate as in a synchronous callback, or it might happen at a later time as in an asynchronous callback. 也就是说,把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调。如果代码立即被执行就称为同步回调,如果在之后晚点的某个时间再执行,则称之为异步回调。关于同步和异步,这里不作讨论,请查阅相关资料。
再来看看来自Stack Overflow某位大神简洁明了的表述:A "callback" is any function that is called by another function which takes the first function as a parameter。 也就是说,函数 F1 调用函数 F2 的时候,函数 F1 通过参数给 函数 F2 传递了另外一个函数 F3 的指针,在函数 F2 执行的过程中,函数F2 调用了函数 F3,这个动作就叫做回调(Callback),而先被当做指针传入、后面又被回调的函数 F3 就是回调函数。到此应该明白回调函数的定义了吧?
2. 为什么要使用回调函数?
很多朋友可能会想,为什么不像普通函数调用那样,在回调的地方直接写函数的名字呢?这样不也可以吗?为什么非得用回调函数呢?有这个想法很好,因为在网上看到解析回调函数的很多例子,其实完全可以用普通函数调用来实现的。要回答这个问题,我们先来了解一下回到函数的好处和作用,那就是解耦,对,就是这么简单的答案,就是因为这个特点,普通函数代替不了回调函数。所以,在我眼里,这才是回调函数最大的特点。来看看维基百科上面我觉得画得很好的一张图片。
下面以一段不完整的 C 语言代码来呈现上图的意思:
实例
#include<stdio.h>
#include<softwareLib.h> // 包含Library Function所在读得Software library库的头文件
int Callback() // Callback Function
{
// TODO
return 0;
}
int main() // Main program
{
// TODO
Library(Callback);
// TODO
return 0;
}
乍一看,回调似乎只是函数间的调用,和普通函数调用没啥区别,但仔细一看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且丝毫不需要修改库函数的实现,这就是解耦。再仔细看看,主函数和回调函数是在同一层的,而库函数在另外一层,想一想,如果库函数对我们不可见,我们修改不了库函数的实现,也就是说不能通过修改库函数让库函数调用普通函数那样实现,那我们就只能通过传入不同的回调函数了,这也就是在日常工作中常见的情况。现在再把main()、Library()和Callback()函数套回前面 F1、F2和F3函数里面,是不是就更明白了?
明白了回调函数的特点,是不是也可以大概知道它应该在什么情况下使用了?没错,你可以在很多地方使用回调函数来代替普通的函数调用,但是在我看来,如果需要降低耦合度的时候,更应该使用回调函数。
3. 怎么使用回调函数?
知道了什么是回调函数,了解了回调函数的特点,那么应该怎么使用回调函数?下面来看一段简单的可以执行的同步回调函数代码。
实例
#include<stdio.h>
int Callback_1() // Callback Function 1
{
for(int i=0; i<1000; i++);printf("Hello, this is Callback_1 \n ");
return 0;
}int Callback_2() // Callback Function 2
{
printf("Hello, this is Callback_2 \n ");
return 0;
}int Callback_3() // Callback Function 3
{
printf("Hello, this is Callback_3 \n ");
return 0;
}int Handle(int (*Callback)())
{
printf("Entering Handle Function. \n ");
Callback();
printf("Leaving Handle Function. \n ");
return 0;
}int main()
{
printf("****** Entering Main Function. ****** \n ");
Handle(Callback_1);
Handle(Callback_2);
Handle(Callback_3);
printf("****** Leaving Main Function. ****** \n ");
return 0;
}
运行结果:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3 Leaving Handle Function. Leaving Main Function.
可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。现在再读一遍这句话:A "callback" is any function that is called by another function which takes the first function as a parameter,是不是就更明白了呢?
4. 怎么使用带参数的回调函数?
眼尖的朋友可能发现了,前面的例子里面回调函数是没有参数的,那么我们能不能回调那些带参数的函数呢?答案是肯定的。那么怎么调用呢?我们稍微修改一下上面的例子就可以了:
实例
#include<stdio.h>
int Callback_1(int x) // Callback Function 1
{
printf("Hello, this is Callback_1: x = %d ", x);
return 0;
}
int Callback_2(int x) // Callback Function 2
{
printf("Hello, this is Callback_2: x = %d ", x);
return 0;
}
int Callback_3(int x) // Callback Function 3
{
printf("Hello, this is Callback_3: x = %d ", x);
return 0;
}
int Handle(int y, int (*Callback)(int))
{
printf("Entering Handle Function. ");
Callback(y);
printf("Leaving Handle Function. ");
}
int main()
{
int a = 2;
int b = 4;
int c = 6;
printf("Entering Main Function. ");
Handle(a, Callback_1);
Handle(b, Callback_2);
Handle(c, Callback_3);
printf("Leaving Main Function. ");
return 0;
}
运行结果:
Entering Main Function. Entering Handle Function. Hello, this is Callback_1: x = 2 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_2: x = 4 Leaving Handle Function. Entering Handle Function. Hello, this is Callback_3: x = 6 Leaving Handle Function. Leaving Main Function.
可以看到,并不是直接把int Handle(int (*Callback)()) 改成 int Handle(int (*Callback)(int)) 就可以的,而是通过另外增加一个参数来保存回调函数的参数值,像这里 int Handle(int y, int (*Callback)(int)) 的参数 y。同理,可以使用多个参数的回调函数。
原文地址:https://www.cnblogs.com/jiangzhaowei/p/9129105.html
C++回调函数的理解与使用
https://www.cnblogs.com/zzw19940404/p/14152151.html
一、回调函数就是一个通过函数指针调用的函数。
如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。
回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
回调函数机制:
1、定义一个函数(普通函数即可);
2、将此函数的地址注册给调用者;
3、特定的事件或条件发生时,调用者使用函数指针调用回调函数。
注:为什么要特定事件或条件发生?不应该随时都可以调用回调函数吗?
以下是回调函数的两种使用方式(简单理解):
1、
#include <stdio.h>
typedef int(*callback)(int,int);int add(int a,int b,callback p){
return (*p)(a,b);
}int add(int a,int b){
return a+b;
}
int main(int argc,char *args[]){
int res = add(4,2,add);
printf("%d\n",res);
return 0;
}
在这个例子中,可以看到,我们定义了一个callbak的函数指针,参数为两个int,返回值为int,通过调用函数地址来进行简单的相加运算。
2、
#include <stdio.h>
typedef int (callBack)(const void *buffer,size_t size,char *p_out);void callFunc(callBack *consume_bytes, char *p_out) {
printf("callFunc\n");
const void *buffer = NULL;
consume_bytes(buffer,0,p_out); //传入值可以随便填
}int callBackFunc(const void *buffer, size_t size, char *p_out){
printf("callBackFunc\n");
memset(p_out,0x00,sizeof(char)*100);
strcpy(p_out,"encoderCallback:this is string.");
return 1;
}int main(int argc,char *args[]){
char p_out[100];
callFunc(callBackFunc,p_out);
printf("%s\n",p_out);
return 0;
}
可以把回调函数和调用函数封装承类再调用。
二、在理解“回调函数”之前,首先讨论下函数指针的概念。
函数指针
(1)概念:指针是一个变量,是用来指向内存地址的。
一个程序运行时,所有和运行相关的物件都是需要加载到内存中,这就决定了程序运行时的任何物件都可以用指针来指向它。
函数是存放在内存代码区域内的,它们同样有地址,因此同样可以用指针来存取函数,把这种指向函数入口地址的指针称为函数指针。
(2)先来看一个Hello World程序:
int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}
然后,采用函数调用的形式来实现:
void Invoke(char* s);
int main(int argc,char* argv[])
{
Invoke("Hello World!\n");
return 0;
}void Invoke(char* s)
{
printf(s);
}
用函数指针的方式来实现:
void Invoke(char* s);
int main()
{
void (*fp)(char* s); //声明一个函数指针(fp)
fp=Invoke; //将Invoke函数的入口地址赋值给fp
fp("Hello World!\n"); //函数指针fp实现函数调用
return 0;
}void Invoke(char* s)
{
printf(s);
}
由上知道:函数指针函数的声明之间唯一区别就是,用指针名(*fp)代替了函数名Invoke,这样这声明了一个函数指针,然后进行赋值fp=Invoke就可以进行函数指针的调用了。声明函数指针时,只要函数返回值类型、参数个数、参数类型等保持一致,就可以声明一个函数指针了。注意,函数指针必须用括号括起来 void (*fp)(char* s)。
实际中,为了方便,通常用宏定义的方式来声明函数指针,实现程序如下:
typedef void (*FP)(char* s);
void Invoke(char* s);int main(int argc,char* argv[])
{
FP fp; //通常是用宏FP来声明一个函数指针fp
fp=Invoke;
fp("Hello World!\n");
return 0;
}void Invoke(char* s)
{
printf(s);
}
函数指针数组
下面用程序对函数指针数组来个大致了解:
#include <iostream>
#include <string>
using namespace std;typedef void (*FP)(char* s);
void f1(char* s){cout<<s;}
void f2(char* s){cout<<s;}
void f3(char* s){cout<<s;}int main(int argc,char* argv[])
{
void* a[]={f1,f2,f3}; //定义了指针数组,这里a是一个普通指针
a[0]("Hello World!\n"); //编译错误,指针数组不能用下标的方式来调用函数FP f[]={f1,f2,f3}; //定义一个函数指针的数组,这里的f是一个函数指针
f[0]("Hello World!\n"); //正确,函数指针的数组进行下标操作可以进行函数的间接调用
return 0;
}
回调函数
(1)概念:回调函数,顾名思义,就是使用者自己定义一个函数,使用者自己实现这个函数的程序内容,然后,把这个函数作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。<< 回调函数:本质上,只是一种“函数调用模型”,一种函数调用机制,一种函数调用的方法。回调函数其实就是“函数指针”的一种用法。
函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,就是由别人的函数运行期间来回调你实现的函数。
(2)标准Hello World程序:
int main(int argc,char* argv[])
{
printf("Hello World!\n");
return 0;
}
将它修改成函数回调样式:
//定义回调函数
void PrintfText()
{
printf("Hello World!\n");
}//定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)())
{
callfuct();
}//在main函数中实现函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText);
return 0;
}
修改成带参的回调样式:
//定义带参回调函数
void PrintfText(char* s)
{
printf(s);
}//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*),char* s)
{
callfuct(s);
}//在main函数中实现带参的函数回调
int main(int argc,char* argv[])
{
CallPrintfText(PrintfText,"Hello World!\n");
return 0;
}
至此,对回调函数应该有了一个大致的了解。
回调函数(callback)是什么?一文理解回调函数(callback)
https://blog.csdn.net/Long_xu/article/details/131320301
这里写目录标题
一、什么是回调函数
1.1、回调函数的定义和基本概念
1.2、回调函数的作用和使用场景
二、回调函数的实现方法
2.1、函数指针
2.2、函数对象/functor
2.3、匿名函数/lambda表达式
三、回调函数的应用举例
四、回调函数的优缺点
五、回调函数与其他编程概念的关系
5.1、回调函数和闭包的关系
5.2、回调函数和Promise的关系
5.3、回调函数和观察者模式的关系
六、如何编写高质量的回调函数
6.1、回调函数的命名规范
6.2、回调函数的参数设计
七、总结
一、什么是回调函数
1.1、回调函数的定义和基本概念
回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。
回调函数通常用于事件处理、异步编程和处理各种操作系统和框架的API。
基本概念:
回调:指被传入到另一个函数的函数。
异步编程:指在代码执行时不会阻塞程序运行的方式。
事件驱动:指程序的执行是由外部事件触发而不是顺序执行的方式。
1.2、回调函数的作用和使用场景
回调函数是一种常见的编程技术,它可以在异步操作完成后调用一个预定义的函数来处理结果。回调函数通常用于处理事件、执行异步操作或响应用户输入等场景。
回调函数的作用是将代码逻辑分离出来,使得代码更加模块化和可维护。使用回调函数可以避免阻塞程序的运行,提高程序的性能和效率。
另外,回调函数还可以实现代码的复用,因为它们可以被多个地方调用。
回调函数的使用场景,包括:
事件处理:回调函数可以用于处理各种事件,例如鼠标点击、键盘输入、网络请求等。
异步操作:回调函数可以用于异步操作,例如读取文件、发送邮件、下载文件等。
数据处理:回调函数可以用于处理数据,例如对数组进行排序、过滤、映射等。
插件开发:回调函数可以用于开发插件,例如 WordPress 插件、jQuery 插件等。
回调函数是一种非常灵活和强大的编程技术,可以让我们更好地处理各种异步操作和事件。
二、回调函数的实现方法
回调函数可以通过函数指针或函数对象来实现。
2.1、函数指针
函数指针是一个变量,它存储了一个函数的地址。当将函数指针作为参数传递给另一个函数时,另一个函数就可以使用这个指针来调用该函数。函数指针的定义形式如下:
返回类型 (*函数指针名称)(参数列表)
例如,假设有一个回调函数需要接收两个整数参数并返回一个整数值,可以使用以下方式定义函数指针:
int (*callback)(int, int);
然后,可以将一个实际的函数指针赋值给它,例如:
int add(int a, int b) {
return a + b;
}
callback = add;
现在,可以将这个函数指针传递给其他函数,使得其他函数可以使用这个指针来调用该函数。
2.2、函数对象/functor
除了函数指针,还可以使用函数对象来实现回调函数。函数对象是一个类的实例,其中重载了函数调用运算符 ()。当将一个函数对象作为参数传递给另一个函数时,另一个函数就可以使用这个对象来调用其重载的函数调用运算符。函数对象的定义形式如下:
class callback {
public:
返回类型 operator()(参数列表) {
// 函数体
}
};
例如,假设有一个回调函数需要接收两个整数参数并返回一个整数值,可以使用以下方式定义函数对象:
class Add {
public:
int operator()(int a, int b) {
return a + b;
}
};
Add add;
然后,可以将这个函数对象传递给其他函数,使得其他函数可以使用这个对象来调用其重载的函数调用运算符。
2.3、匿名函数/lambda表达式
回调函数的实现方法有多种,其中一种常见的方式是使用匿名函数/lambda表达式。
Lambda表达式是一个匿名函数,可以作为参数传递给其他函数或对象。在C++11之前,如果想要传递一个函数作为参数,需要使用函数指针或者函数对象。但是这些方法都比较繁琐,需要显式地定义函数或者类,并且代码可读性不高。使用Lambda表达式可以简化这个过程,使得代码更加简洁和易读。
下面是一个使用Lambda表达式实现回调函数的例子:
#include <iostream>
#include <vector>
#include <algorithm>void print(int i) {
std::cout << i << " ";
}void forEach(const std::vector<int>& v, const void(*callback)(int)) {
for(auto i : v) {
callback(i);
}
}int main() {
std::vector<int> v = {1,2,3,4,5};
forEach(v, [](int i){std::cout << i << " ";});
}
在上面的例子中,我们定义了一个forEach函数,接受一个vector和一个回调函数作为参数。回调函数的类型是void()(int),即一个接受一个整数参数并且返回void的函数指针。在main函数中,我们使用了Lambda表达式来作为回调函数的实现,即[](int i){std::cout << i << " ";}。Lambda表达式的语法为{/ lambda body */},其中[]表示Lambda表达式的捕获列表,即可以在Lambda表达式中访问的外部变量;{}表示Lambda函数体,即Lambda表达式所要执行的代码块。
在使用forEach函数时,我们传递了一个Lambda表达式作为回调函数,用于输出vector中的每个元素。当forEach函数调用回调函数时,实际上是调用Lambda表达式来处理vector中的每个元素。这种方式相比传递函数指针或者函数对象更加简洁和易读。
使用Lambda表达式可以方便地实现回调函数,使得代码更加简洁和易读。但是需要注意Lambda表达式可能会影响代码的性能,因此需要根据具体情况进行评估和选择。
三、回调函数的应用举例
异步编程中的回调函数:网络编程中,当某个连接收到数据后,可以使用回调函数来处理数据。
例如:
void onDataReceived(int socket, char* data, int size);
int main() {
int socket = connectToServer();
startReceivingData(socket, onDataReceived);
// ...
}void onDataReceived(int socket, char* data, int size) {
// 处理数据...
}
回调函数在GUI编程中的应用:GUI编程中,当用户触发了某个操作时,可以使用回调函数来处理该操作。
例如:
void onButtonClicked(Button* button);
int main() {
Button* button = createButton("Click me");
setButtonClickHandler(button, onButtonClicked);
// ...
}void onButtonClicked(Button* button) {
// 处理按钮点击事件...
}
事件处理程序中的回调函数:多线程编程中,当某个线程完成了一次任务后,可以使用回调函数来通知主线程。
例如:
void onTaskCompleted(int taskId);
int main() {
for (int i = 0; i < numTasks; i++) {
startBackgroundTask(i, onTaskCompleted);
}
// ...
}void onTaskCompleted(int taskId) {
// 处理任务完成事件...
}
四、回调函数的优缺点
优点:
提高代码的复用性和灵活性:回调函数可以将一个函数作为参数传递给另一个函数,从而实现模块化编程,提高代码的复用性和灵活性。
解耦合:回调函数可以将不同模块之间的关系解耦,使得代码更易于维护和扩展。
可以异步执行:回调函数可以在异步操作完成后被执行,这样避免了阻塞线程,提高应用程序的效率。
缺点:
回调函数嵌套过多会导致代码难以维护:如果回调函数嵌套层数过多,代码会变得非常复杂,难以维护。
回调函数容易造成竞态条件:如果回调函数中有共享资源访问,容易出现竞态条件,导致程序出错。
代码可读性差:回调函数的使用可能会破坏代码的结构和可读性,尤其是在处理大量数据时。
小结:代码灵活、易于扩展,但是不易于阅读、容易出错。
五、回调函数与其他编程概念的关系
5.1、回调函数和闭包的关系
回调函数和闭包之间存在着紧密的关系。回调函数是一个函数,在另一个函数中被作为参数传递,并在该函数执行完后被调用。
闭包是由一个函数及其相关的引用环境组合而成的实体,可以访问函数外部的变量。
在某些情况下,回调函数需要访问到它所在的父函数的变量,这时就需要使用闭包来实现。通过将回调函数放在闭包内部,可以将父函数的变量保存在闭包的引用环境中,使得回调函数能够访问到这些变量。同时,闭包还可以保证父函数中的变量在回调函数执行时不会被销毁,从而确保了回调函数的正确性。
因此,回调函数和闭包是一对密切相关的概念,常常一起使用来实现复杂的逻辑和功能。
5.2、回调函数和Promise的关系
C++回调函数和Promise都是异步编程的实现方式。
回调函数是一种将函数作为参数传递给另一个函数,在异步操作完成后执行的技术。在C++中,回调函数通常使用函数指针或函数对象来实现。当异步操作完成后,会调用注册的回调函数,以便执行相应的处理逻辑。
而Promise则是一种更加高级的异步编程模式,它通过解决回调地狱问题,提供了更加优雅和简洁的异步编程方式。Promise可以将异步操作封装成一个Promise对象,并通过链式调用then()方法来注册回调函数,以及catch()方法来捕获异常。当异步操作完成后,Promise会自动根据操作结果触发相应的回调函数。
因此,可以说C++回调函数和Promise都是异步编程的实现方式,但是Promise提供了更加高级和优雅的编程模式,能够更好地管理异步操作和避免回调地狱问题。
5.3、回调函数和观察者模式的关系
回调函数和观察者模式都是用于实现事件驱动编程的技术。它们之间的关系是,观察者模式是一种设计模式,它通过定义一种一对多的依赖关系,使得一个对象的状态发生改变时,所有依赖于它的对象都会得到通知并自动更新。而回调函数则是一种编程技术,它允许将一个函数作为参数传递给另一个函数,在执行过程中调用这个函数来完成特定的任务。
在观察者模式中,当一个被观察的对象发生改变时,会遍历所有的观察者对象,调用其定义好的更新方法,以进行相应的操作。这里的更新方法就可以看做是回调函数,因为它是由被观察对象调用的,并且在执行过程中可能需要使用到一些外部参数或上下文信息。因此,可以说观察者模式本身就包含了回调函数的概念,并且借助回调函数来实现观察者模式的具体功能。
六、如何编写高质量的回调函数
回调函数需要遵循以下几个原则:
- 明确函数的目的和作用域。回调函数应该有一个清晰的目的,同时只关注与其作用范围相关的任务。
- 确定回调函数的参数和返回值。在定义回调函数时,需要明确它所需的参数和返回值类型,这样可以使调用方更容易使用。
- 谨慎处理错误和异常。回调函数可能会引发一些异常或错误,需要使用 try-catch 块来处理它们,并给出相应的警告。
- 确保回调函数不会导致死锁或阻塞。回调函数需要尽可能快地执行完毕,以避免影响程序的性能和稳定性。
- 使用清晰且易于理解的命名规则。回调函数的命名应该清晰、简洁,并尽可能说明其功能和意义。
- 编写文档和示例代码。良好的文档和示例代码可以帮助其他开发者更容易地使用回调函数,同时也有助于提高代码的可维护性和可重用性。
- 遵循编码规范和最佳实践。 编写高质量的回调函数需要遵守编码规范和最佳实践,例如使用合适的命名规则、注释代码等。
6.1、回调函数的命名规范
回调函数的命名规范没有固定的标准,但是根据通用惯例和编码规范,回调函数的命名应该能够反映函数的作用和功能,让其他开发者能够快速理解并使用。
- 使用动词+名词的方式来描述回调函数的作用,例如onSuccess、onError等。
- 如果回调函数是用于处理事件的,可以以handleEvent或者onEvent作为函数名。
- 如果回调函数是用于处理异步操作完成后的结果,可以以onComplete或者onResult作为函数名。
- 在命名时要注意保持简洁明了,不要过于冗长,也不要使用缩写或者不清晰的缩写。
- 尽量使用有意义的单词或者短语作为函数名,不要使用无意义的字母或数字组合。
- 与代码中其他的函数名称保持一致,尽量避免出现命名冲突的情况。
6.2、回调函数的参数设计
回调函数的参数设计取决于回调函数所需执行的操作和数据。一般来说,回调函数需要接收至少一个参数,通常是处理结果或错误信息。其他可选参数根据需要添加。
例如,如果回调函数是用于处理异步请求的,则第一个参数可能是错误信息(如果存在),第二个参数则是请求返回的数据。另外,也可以将回调函数的上下文传递给该函数作为参数,以便在回调函数中使用。
假设有一个函数 process_data 用于处理数据,但是具体的处理方式需要根据不同的情况进行定制化。这时候我们可以使用回调函数来实现。
回调函数的参数设计如下:
void process_data(void *data, int len, void (*callback)(void *result));
其中,data 表示要处理的数据,len 表示数据的长度,callback 是一个函数指针,用于指定处理完数据后的回调函数。回调函数的形式如下:
void callback_func(void *result);
在 process_data 函数中,首先会对数据进行处理,然后将处理结果传递给回调函数进行处理。具体实现如下:
void process_data(void *data, int len, void (*callback)(void *result)) {
// 处理数据
void *result = data; // 这里只是举个例子,实际上需要根据实际情况进行处理// 调用回调函数
callback(result);
}
使用示例:
#include <stdio.h>
void callback_func(void *result) {
printf("processing result: %s\n", (char *)result); // 这里只是举个例子,实际上需要根据实际情况进行处理
}int main() {
char data[] = "hello world";
process_data(data, sizeof(data), callback_func);
return 0;
}
七、总结
回调函数是一种常见的编程模式,主要内容包括以下几个方面:
回调函数的定义:回调函数是一个作为参数传递给其他函数的函数,它能够被异步调用以处理某些事件或完成某些任务。
回调函数的使用场景:回调函数通常用于异步编程中,例如在浏览器端的 AJAX 请求、Node.js 中的文件读写等场景中都会使用回调函数。
回调函数的实现方式:回调函数可以通过直接传入函数名或者通过匿名函数的方式来实现。
回调函数的错误处理:在回调函数中,需要对可能出现的错误进行处理,例如返回错误对象、抛出异常或通过回调函数传递错误信息等方式。
回调函数的优缺点:回调函数可以提高代码的灵活性和可重用性,但也容易导致代码复杂度增加、嵌套过深等问题。
————————————————
版权声明:本文为CSDN博主「Lion Long」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/Long_xu/article/details/131320301