回调函数(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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值