网上查了一通,有点体会,特来分享与讨论。

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

SECTION 1

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

回调函数

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

库与使用者的问题

在开始之前,首先我们想像这样一个情景,一个大型软件公司开发一套软件库提供给用户使用。在这句话中,出现两个对立面,一个是软件公司,一个是用户。显然,软件公司实力很强大,他们强大的地方在于他们很聪明。请看下图:

库开发者是提供方,他们不知道用户想做什么,库开发者对用户的信息是未知的,因为两边的人谁也没见过谁,就像一个人在美国一个人在中国。他们聪明的地方就在于即使不知道用户怎么操作,他们也可以用一种通用的方法来解决用户的问题。换句话说,库开发方不管用户怎么折腾变化,他都能适应。这就是问题的神奇之处,当然,为了达到这种效果,必须有一种规则来约束。

规则 

现在来说规则,规则比较好理解,就是两方达成的约束。比如甲和乙两个人,甲对乙说,你只要提供给我一个塑料球,我都能给它涂上颜色。这里的约束是塑料球,(当然,为了简化问题这里的约束还是比较泛泛而不严谨的),这个时候如果乙给他一个水晶球,或者给他一辆汽车都不行,因为已经超越了约束。所以说,库开发者能适应用户的各种变化就在于他也给了用户一定的约束,是在约束之下的适应,这也是不言自明的。

代码之下无秘密

下面一段代码极好的解释了上述问题:

#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里面,这样就和它有了界限。

库开发者提供方法sort(...)供我们调用,形参里面还包括一个函数指针,就是调用用户自己写的函数,也即回调函数。所以它提供了规则,也就是说这个回调函数的形参是什么,返回值又是什么,假如用户对于这个回调函数的形参和返回值啥都不知道,随意写了一个函数放进去,sort方法能接受吗?显然是不行的。

那么用户怎么知道回调函数的形参和返回值等信息呢?库开发方会告知,或者在它的文档里面会有说明,就像在windows API开发窗口程序时提供的WinProc() 函数一样,里面的形参必须一模一样。(这个地方会牵涉到接口)

什么是回?

根据上面的代码,我们知道,库开发方提供了一个方法,然后形参又带有一个函数指针,等着用户去写然后拿来调用,并在其实现中调用了这个函数。站在库开发方的立场,我们可以总结为一句话:库开发方调用了用户的函数(回调函数)。

现在,我们把视角移到回调函数的立场上来看,它被调用了。但同时它也有参数,且传进来的参数是库开发方提供的数据,这里我们又可以总结一句话:回调函数调用了库开发方的数据。

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

回调函数的重点是什么?

根据上文分析,发现回调函数其实也不是难于理解,我觉得重点在于一定要划分清楚谁是库开发方(也可称为提供方,调用者),谁是用户,把职能划分清楚是最关键的。

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

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>
  1. typedef void (*Fun)(int);//定义一个函数指针类型
  1. Fun p = NULL;//Fun定义一个变量p,它指向一个返回值为空参数为int的函数
  1. void caller(Fun pCallback)
  1. {
  2. p = pCallback;
  1. //达成某一条件后,通过名片(函数指针p),传回结果
  1. int result = 1;
  1. (*p)(result);
  2. }
  1. void callback(int a)//回调函数
  1. {
  1. std::cout << "callback result = " << a << std::endl;
  1. }
  1. int main(int argc, char* argv[])
  1. {
  2. caller(callback);
  3. getchar();
  1. return 0;
  1. }

怎么理解回调函数? 回调函数合集相关推荐

  1. 面试题:函数回调机制、异步函数回调机制图例详解 没毛用

    函数回调机制,一种双向调用思想,简单来说就是,如下图所示: 在层次一中的方法一(函数)调用层次二中的方法,并传入函数二的地址,而这个被调用的方法又会调用层次一中的方法,这个最后被调用的方法二就是回调方 ...

  2. Python 数据分析三剑客之 NumPy(五):数学 / 算术 / 统计 / 排序 / 条件 / 判断函数合集

    CSDN 课程推荐:<Python 数据分析与挖掘>,讲师刘顺祥,浙江工商大学统计学硕士,数据分析师,曾担任唯品会大数据部担任数据分析师一职,负责支付环节的数据分析业务.曾与联想.亨氏.网 ...

  3. RTT设置删除空闲钩子函数想到函数指针和回调函数

    目录 一.概述 二.函数指针 2.1 函数指针的使用 2.2 * (int * )&p 三.回调函数 一.概述 学习RTT时,看到设置和删除空闲钩子函数接口: rt_err_t rt_thre ...

  4. JNA实战笔记汇总(二)——JNA和C / C ++的数据类型映射(dll函数回调、结构体、指针)

    目录 JNA技术难点 1.函数回调 2.结构体 3.指针 JNA技术难点 有过跨平台.跨语言开发的程序员都知道,跨平台.预研调用的难点,就是不同语言之间数据类型不一致造成的问题.绝大部分跨平台调用的失 ...

  5. c语言中的钩子函数,回调函数以及钩子函数的概念

    钩子实际上是一个处理消息的程序段,通过系统调用,把它挂入系统.每当特定的消息发出,在没有到达目的窗口前,钩子程序就先捕获该消息,亦即钩子函数先得到控制权.这时钩子函数即可以加工处理(改变)该消息,也可 ...

  6. 秒懂函数回调机制,回调函数看这篇就够了

    什么是回调函数 友情提示:原理介绍部分摘自:https://www.jianshu.com/p/2f695d6fd64f 有一定基础的直接跳过即可,直接查看后面精彩部分... 回调函数就是一个通过函数 ...

  7. python退出时回调_python 回调函数和回调方法的实现分析

    回调与事件驱动 回调函数有比较重要的意义:它在是事件驱动的体现 我们试想一个场景,如果我们触发了某个事件,比如点击事件 那么只要给这个点击事件绑定一个或多个处理事件,也就是回调函数 我们就可以在点击事 ...

  8. 回调函数 同步回调 异步回调

    回调函数 回调函数一般是在封装接口的时候,回调显得特别重要,我们首先假设有两个程序员在写代码,A程序员写底层驱动接口,B程序员写上层应用程序,然而此时底层驱动接口A有一个数据d需要传输给B,此时有两种 ...

  9. c语言函数指针,基于函数回调模拟实现qsort函数,实测案例

    前言 qsort函数C语言编译器函数库自带的排序函数. qsort 的函数原型是void qsort (void*base,size_t num,size_t width,int (__cdecl*c ...

最新文章

  1. kinect+java3d+robot开发备忘录
  2. 录入键盘字母输入并将小写变为大写
  3. 把时间当作朋友(四)
  4. 爱情第五课,终极选择题
  5. MySQL如何向表中增加、修改字段
  6. VS Code运行Python程序
  7. 对话Justin Uberti:RTC的过去、现在和未来
  8. c语言编译器怎么用scanfkl,C语言一些笔记
  9. zabbix分布式监控环境完全编译安装部署
  10. 印刷体是什么意思_家长晒出4年级小学霸课前笔记,字迹堪比“印刷体”,老师都羡慕...
  11. PCA和线性回归之间的关系如何?
  12. (75)Xilinx Slice组成结构有哪些?
  13. 数据分析没价值?——深思对业务的洞察有多少?
  14. iTerm2 + oh my zsh +agnoster 打造最强Mac终端
  15. Navicat Premium 12安装过程
  16. 计算机专业范文推荐信,出国留学推荐信范文,计算机专业
  17. 动态启用和禁用mainfest中组件
  18. iWebOffice2015入门(二)
  19. BASIC-9 特殊回文数 C语言版
  20. Windows系统文件结构

热门文章

  1. Docker 配置固定IP及桥接的实现方法(转载)
  2. 数据库——MongoDB增删改查
  3. MS SQL收縮資料庫
  4. python库——h5py入门讲解
  5. 一个不错的shell 脚本教程 入门级
  6. 5.14 图像频域处理
  7. 【转】IAR与Keil两款开发工具区别
  8. 安装双系统(win10+Ubuntu18.0)使用一段时间后,就直接进入win10,bios启动项里也没有Ubuntu
  9. Linux系统中查看图片信息
  10. 医疗软件产品核心算法部分说明--转载截取