当前位置: 首页 > 图灵资讯 > 技术篇> 怎么理解回调函数? 回调函数合集

怎么理解回调函数? 回调函数合集

来源:图灵教育
时间:2023-05-21 09:14:24

我在网上查了一下,有一点经验,特意分享讨论。

****************************************************************************************************************************

SECTION 1

****************************************************************************************************************************

回调函数

回调函数是一个总是听到的概念,比如windows API编程中遇到的WinProc函数是由操作系统编写调用的函数。现在,我们需要慢慢详细地记录这个问题。

库与用户之间的问题

在开始之前,我们首先想象一下,一家大型软件公司为用户开发了一套软件库。在这句话中,有两个对立面,一个是软件公司,一个是用户。显然,软件公司的实力非常强大,他们的实力在于他们非常聪明。请参见下图:

图书馆开发人员是提供商。他们不知道用户想做什么。图书馆开发人员对用户的信息是未知的,因为双方都没有人见过任何人,就像一个人在美国和中国一样。他们的聪明之处在于,即使他们不知道如何操作用户,他们也可以用一种通用的方法来解决用户的问题。换句话说,无论用户如何折腾变化,库开发商都能适应。这就是问题的魔力。当然,为了达到这种效果,必须有一条规则来约束它。

规则

现在,规则更容易理解,即双方达成的约束。例如,A和B,A和B说,只要你给我一个塑料球,我就可以给它涂上颜色。这里的约束是塑料球,(当然,为了简化这里的约束相对普遍和不严格),如果B给了他一个水晶球,或者给了他一辆车,因为它已经超出了约束。因此,库开发人员能够适应用户的各种变化是,他也给了用户一定的约束,这是不言而喻的。

代码下没有秘密

以下代码很好地解释了上述问题:

怎么理解回调函数? 回调函数合集_回调函数

#include <stdio.h>typedef int student_id; typedef int student_age; typedef struct _Student{     student_id id;     student_age age; }Student;//类型重定义:函数指针类型 typedef bool (*pFun)(Student, Student);//----------------------------------------------- //冒泡排序法:可按AGE或ID排序,用同一函数实现 //----------------------------------------------- void sort(Student stu[],const int num,pFun fun) {     Student temp;         for(int i = 0; i < num; ++i){         for(int j = 0; j < num - i -1; ++j)         {             if((*fun)(stu[j],stu[j+1]))             {                 temp = stu[j];                 stu[j] = stu[j+1];                 stu[j+1] = temp;             }         }     } }//----------------------------------------------- //回调函数:比较年龄 //----------------------------------------------- bool CompareAge(Student stu1,Student stu2) {     //改变从大到小或从小到大的顺序,只需反过来。     if(stu1.age < stu2.age)         return true;     return false; } //----------------------------------------------- //回调函数:比较id //----------------------------------------------- bool CompareId(Student stu1,Student stu2) {     //改变从大到小或从小到大的顺序,只需反过来。     if(stu1.id < stu2.id)         return true;     return false; }int main() {     Student stu[] = {     {1103,24},     {1102,23},     {1104,22},     {1107,25},     {1105,21}};         pFun fun = CompareAge;     int size = sizeof(stu)/sizeof(Student);         sort(stu,size,fun);         for(int i = 0; i < size; ++i){         printf("%d %d\n",stu[i].id,stu[i].age);     }         return 0; }

通过代码,我们必须区分哪个是库提供商,哪个是用户:

sort(...)方法是库开者仅仅因为模拟问题,就把它写在一个文件里。我们可以假设它存储在静态库lib中,这样它就有了界限。

Sortt开发人员提供方法(...)供我们调用,形参还包括一个函数指针,即调用用户自己写的函数,即回调函数。所以它提供了规则,也就是说,这个回调函数的形参是什么,返回值是什么?如果用户对这个回调函数的形参和返回值一无所知,可以随意写一个函数放进去吗?显然不行。

那么,用户如何知道回调函数的形状参考和返回值呢?库开发人员会告知或解释它的文档,就像在windows一样 WinProc()是API开发窗口程序时提供的 函数一样,里面的形参必须一模一样。(这个地方会涉及到接口)

什么是回?

根据上面的代码,我们知道图书馆开发人员提供了一种方法,然后形状参考有一个函数指针,等待用户写作和呼叫,并在实际实现中呼叫函数。站在仓库开发商的立场,我们可以总结为一句话:库开发人员调用用户函数(回调函数)。

现在,我们把视角转移到了回调函数的位置从上面看,它被调用了。但同时也有参数,传输的参数是库开发人员提供的数据。在这里,我们可以总结一句话:回调函数调用库开发人员的数据。

这就是“回”你调我,我也调用你,双方互相调用。

怎么理解回调函数? 回调函数合集_开发者_02

回调函数的重点是什么?

根据以上分析,发现回调函数并不难理解。我认为重点是区分谁是图书馆开发人员(也称为供应商和调用人员),谁是用户职能清晰的划分是最关键的。

****************************************************************************************************************************

SECTION 2

****************************************************************************************************************************

tip1

你只是想象你函数的一部分功能被外包给别人。至于如何实现,你不必担心。您的函数具有完整的功能,但有些功能可以根据stl中的for_定制each

tip2

简单来说,用户是实现方,需要调用A()函数,但为了A()函数的通用性,需要根据实现方的意愿调用实现方提供的函数CBB(),这里的CBB()就是回调函数。回调函数广泛应用于Windows编程中。

****************************************************************************************************************************

SECTION 3

****************************************************************************************************************************

回调函数是通过函数指针调用的函数:将函数的指针(地址)作为参数传递给另一个函数,当该指针用于调用其指向的函数时,称为回调函数。回调函数不是由函数的实现者直接调用的,而是由另一方在特定事件或条件发生时调用的,以响应事件或条件。

一般来说,在A类中调用B类中的某种方法C,然后B类反过来调用A类中的方法D,D就是回调函数。

打个比方:

我们把A类当成一个人,叫他小A;把B类也当成一个人,叫他小B类;

然后可以理解使用回调函数D的过程:

小A在开发过程中遇到了一个麻烦,只有小B才能解决,所以小A找到了小B寻求帮助,但因为他不太熟悉小B,所以他带了一张名片。小A向小B解释了遇到的麻烦后,碰巧小B忙于其他事情,于是小B先收到小A的名片,告诉小A回去等消息。因为这个麻烦不解决就不能继续发展,所以回去等消息的小A不得不先做其他事情。过了一段时间,小B忙于手头的事情,解决了小A的麻烦,找到了小A名片上的电话号码,告诉小A,麻烦已经解决了(小B只是跟着名片告诉小A解决方案,不关心他们给小A的解决方案会如何使用)。小A放下电话后,用小B给他的解决方案继续开发。

简而言之:小A带着名片D通过C找到小B寻求帮助,小B不能立即解决,所以他接受了名片D,有一天小B解决了小A的问题,然后通过名片D告诉小A解决方案。

即:

A类调用B类中的C方法,D作为函数指针作为C方法的参数(小A带着名片D通过C找到小B寻求帮助)

B类不能立即处理,先标记回调函数(接收名片)

在未来的某个时间点,当触发条件满足时(解决问题后)

信息通过回调函数D传递给A类(通过名片告知结果)

以下是上述过程的一个例子(解释见注释):

1. #include <iostream>2. typedef void (*Fun)(int);//定义函数指针类型3. Fun p = NULL;//用Fun定义一个变量p,它指向空参数为int的函数4的返回值. void caller(Fun pCallback)5. {6. p = pCallback;7. //通过名片(函数指针p)达到一定条件后,传回结果8. int result = 1;9. (*p)(result);10. }11. void callback(int a)//回调函数12. {13. std::cout << "callback result = " << a << std::endl;14. }15. int main(int argc, char* argv[])16. {17. caller(callback);18. getchar();19. return 0;20. }