精华内容
下载资源
问答
  • 一组回调函数
    千次阅读
    2021-04-19 06:34:36

    1、从最简单的开始

    编程最基本的目的就是——我执行一个操作,程序做出一个反应。“一个操作”包括:点击鼠标,拖动滑块,填写数据,选择选项…… “做出一个反应”包括:计算一些东西,然后储存在哪里,或者贴个图出来,或者显示在哪里……

    --------------------------------------------------------------------------------------------------

    编程的基本思想:

    目的->当点击按钮A时,然后执行任务C, 则->进入按钮A的回调函数callback里,写下任务C的代码。

    --------------------------------------------------------------------------------------------------

    a:不知道callback在哪??右键单击按钮,view callbacks->选择callback)别的控件也一样。

    a:不同的回调函数啥意思??

    callBack

    最常用的就是它。如果控件是按钮,那点击按钮时,则按钮下的Callback就会执行;如果是滑块,则拖动滑块时,滑块名下的callback就会执行;总之,就是对控件默认操作时,matlab后台就会自动调用它名下的的callback。正常用途,全放在callback下就够了。如果其他的,实在是想知道,了解一下也未尝不可。

    ButtonDownFcn

    就是鼠标在它(代指各种控件)上面点击一下,你放在这个函数名下的代码就会执行。(ps:按钮的callback也是点击,所以会覆盖掉这个buttondownfcn。)

    CreateFcn

    顾名思义,在生成这个控件,显示之前,执行你放在这个函数名下的代码;

    DeleteFcn

    一样。在控件要销毁,但是被毁灭之前执行这个函数名下的代码。貌似“真的要退出吗”这一类就是写在这里。

    KeyPressFcn

    当前控件获得焦点且有按键按下时执行。(什么是焦点?就是这个控件被鼠标点了(或者是tab轮选到了)。你打开一个记事本,然后再点这个网页,再随便按几个按键,记事本上有字不?没有对吧。因为它没获得焦点,所以你的按键它不会反应的。只有你最后再点它一下,再按几个按键,结果怎么样?有字了吧?)这里的KeyPressFcn也是一样的道理。

    一般在这下面的代码还会判断下按键是什么,然后执行相应的代码。

    ResizeFcn,SelectionChangeFcn 不常用。也懒得去看了。附几句,控件都有Resize的属性,好像默认是不可调整,如果要用到这个函数,去把它打开先。

    SelectionChangeFcn

    是在群按钮 组件中,改变选择时,所执行的函数。

    哪些控件支持哪些回调函数?很简单,你在它上面右键就可以看到了,点进去就可以编辑了。

    可以在property inspect中在需要的回调函数下设置%automatic就可以自动添加相应的回调函数框架。

    2、常用控件代码框架参考

    ---------------------------------------------------------------------

    Toggle Button 的callback一般写法

    toggle button是什么?设计面板上,左边那一栏图标,看到它名字了吧?没看到?File->preference->GUIDE->把那个show names那一栏勾选上。好了,可以看到了吧 。知道是什么了吧?

    拖进来,双击它,看到它的属性设置表。上面各种各样的属性,以后有时间慢慢研究。

    点下它时,它默认value ='Max'

    不点下它时,它value='Min'

    1.functon togglebutton1_callback(hObject,eventdata,handles)

    2.%这一句,现在不用管。自动加的。

    3.%%%以下是一般代码

    4.button_state = get(hObject,'Value')

    5.%控件甲的callback下面hObject就是控件甲,同理控件乙callback下的hObject......

    6.if button_state==get(hObject,'Max')

    7. %则怎么怎么怎么

    8.else if button_state==get(hObject,'Min')

    9. %则怎么怎么怎么

    10.end

    --------

    备注:button_state随便取的变量名,存放按钮的选择状态,别以为它是什么喔。

    如果你的button甲是放在一个buttonGroup里面,则button甲callback下的代码就要挪到buttonGroup下面去。因为buttonGroup的回调函数会覆盖掉它成员的回调函数。

    ----------------------------------------

    Radio Buttons

    ------

    1.右键点选你的Radio按钮,进入编辑callback

    2.function ………………略

    3.if(get(hObject,'Value')==get(hObject,'Max'))

    4. %被选中了,你想怎么样

    5.else

    6. %不被选中,你想怎么样

    7.end

    -----------------------------------------

    Check Boxes

    -----------------------------------------

    1.if(get(hObject,'Value')==get(hObject,'Max'))

    2. %被选中了,你想怎么样

    3.else

    4. %不被选中,你想怎么样

    5.end

    更多相关内容
  • 回调函数详解

    万次阅读 多人点赞 2018-12-04 19:26:53
    、什么是回调函数   回调函数是指 使用者自己定义个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你...

    一、什么是回调函数

      回调函数是指 使用者自己定义一个函数,实现这个函数的程序内容,然后把这个函数(入口地址)作为参数传入别人(或系统)的函数中,由别人(或系统)的函数在运行时来调用的函数。函数是你实现的,但由别人(或系统)的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。简单来说,当发生某种事件时,系统或其他函数将会自动调用你定义的一段函数

    二、回调函数详解

    1、C语言中的回调函数

      回调函数主要结构有三部分组成:主函数、调用函数和被调函数。C语言中,被调函数通常以函数指针(指向对应函数的入口地址)的形式出现。

    //定义回调函数
    void PrintfText() 
    {
        printf("Hello World!\n");
    }
    
    //定义实现回调函数的"调用函数"
    // 参数为函数指针,无参数返回void
    void CallPrintfText(void (*callfuct)())
    {
        callfuct();
    }
    
    //实现函数回调
    int main(int argc,char* argv[])
    {
        CallPrintfText(PrintfText);
        return 0;
    }
    

      调用函数向其函数中传递void (*callfuct)(void)这是一个void callfuct(void)函数的入口地址,即PC指针可以通过移动到该地址执行void callfuct(void)函数。
      实现函数调用中,函数调用了“调用函数”,再在其中进一步调用被“调用函数”。相比于主函数直接调用“被调函数”,这种方法为使用者,而不是开发者提供了灵活的接口。另外,函数入口可以像变量一样设定同样为开发者提供了灵活性。

    2、C++中的回调函数

    bool myIsShorter(const string &s1, const string &s2)
    {    
         return s1.size() < s2.size();
    }
    // 前面两个是普通参数,即迭代器,第三个参数isShorter就是回调函数
    // 在传递myIsShorter这个参数时,只需写函数名,它代表函数指针
    stable_sort(words.begin(), words.end(), myIsShorter);
    

      stable_sort调用了myIsShorter,而myIsShorter又调用了stable_sort给它的单词,它们相互调用。这就是“回调”这两个字的含义。

    3、回调函数的机制

    (1) 定义一个回调函数;

    (2)提供函数实现的一方在初始化的时候,将回调函数的函数指针注册给调用者;

    (3)当特定的事件或条件发生的时候,调用者使用函数指针调用回调函数对事件进行处理。

    三、为什么要用回调函数

      因为可以把调用者与被调用者分开。调用者不关心谁是被调用者,所有它需知道的,只是存在一个具有某种特定原型、某些限制条件(如返回值为int)的被调用函数。

      如果想知道回调函数在实际中有什么作用,先假设有这样一种情况,我们要编写一个库,它提供了某些排序算法的实现,如冒泡排序、快速排序、归并排序等等,但为使库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,想让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。

      回调可用于通知机制,例如,有时要在程序中设置一个计时器,每到一定时间,程序会得到相应的通知,但通知机制的实现者对我们的程序一无所知。而此时,就需有一个特定原型的函数指针,用这个指针来进行回调,来通知我们的程序事件已经发生。实际上,SetTimer()API使用了一个回调函数来通知计时器,而且,万一没有提供回调函数,它还会把一个消息发往程序的消息队列。

    四、回调函数的优点

    • 可以让实现方,根据回调方的多种形态进行不同的处理和操作。

    • 可以让实现方,根据自己的需要定制回调方的不同形态。

    • 可以将耗时的操作隐藏在回调方,不影响实现方其它信息的展示。

    • 让代码的逻辑更加集中,更加易读。

    五、回调函数总结

      总之,所谓回调函数就是把函数当作参数使用目的是使程序更加普适(正如活字印刷,把可能会“变”的字一个个分离开来,这样就可以任意组合,重复利用)。一般情况下,一个人的小规模程序用不着这种普适性,除非你想把它做成工具箱(比如游戏引擎),供他人使用。

    展开全文
  • C语言回调函数详解(全网最全)

    万次阅读 多人点赞 2021-10-31 00:10:41
    4.下面是个四则运算的简单回调函数例子:5. 回调函数实例(很有用) 、函数指针 在讲回调函数之前,我们需要了解函数指针。 我们都知道,C语言的灵魂是指针,我们经常使用整型指针,字符串指针,结构体指针等 ...

    一、函数指针

    在讲回调函数之前,我们需要了解函数指针。

    我们都知道,C语言的灵魂是指针,我们经常使用整型指针,字符串指针,结构体指针等

    int *p1;
    char *p2;
    STRUCT *p3; //STRUCT为我们定义的结构体
    

    但是好像我们一般很少使用函数指针,我们一般使用函数都是直接使用函数调用。

    下面我们来了解一下函数指针的概念和使用方法。

    1.概念

    函数指针是指向函数的指针变量。

    通常我们说的指针变量是指向一个整型、字符型或数组等变量,而函数指针是指向函数。

    函数指针可以像一般函数一样,用于调用函数、传递参数。

    函数指针的定义方式为:

    函数返回值类型 (* 指针变量名) (函数参数列表);

    “函数返回值类型”表示该指针变量可以指向具有什么返回值类型的函数;“函数参数列表”表示该指针变量可以指向具有什么参数列表的函数。这个参数列表中只需要写函数的参数类型即可。

    我们看到,函数指针的定义就是将“函数声明”中的“函数名”改成“(*指针变量名)”。但是这里需要注意的是:“(*指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个函数声明了,即声明了一个返回值类型为指针型的函数。

    那么怎么判断一个指针变量是指向变量的指针变量还是指向函数的指针变量呢?首先看变量名前面有没有“”,如果有“”说明是指针变量;其次看变量名的后面有没有带有形参类型的圆括号,如果有就是指向函数的指针变量,即函数指针,如果没有就是指向变量的指针变量。

    最后需要注意的是,指向函数的指针变量没有 ++ 和 – 运算。

    一般为了方便使用,我们会选择

    typedef 函数返回值类型 (* 指针变量名) (函数参数列表);

    比如

    typedef int (*Fun1)(int);//声明也可写成int (*Fun1)(int x),但习惯上一般不这样。
    typedef int (*Fun2)(int, int);//参数为两个整型,返回值为整型
    typedef void (*Fun3)(void);//无参数和返回值
    typedef void* (*Fun4)(void*);//参数和返回值都为void*指针
    

    2,如何用函数指针调用函数

    给大家举一个例子:

    int Func(int x);   /*声明一个函数*/
    int (*p) (int x);  /*定义一个函数指针*/
    p = Func;          /*将Func函数的首地址赋给指针变量p*/
    p = &Func;          /*将Func函数的首地址赋给指针变量p*/
    

    赋值时函数 Func 不带括号,也不带参数。由于函数名 Func 代表函数的首地址,因此经过赋值以后,指针变量 p 就指向函数 Func() 代码的首地址了。

    下面来写一个程序,看了这个程序你们就明白函数指针怎么使用了:

    #include <stdio.h>
    int Max(int, int);  //函数声明
    int main(void)
    {
        int(*p)(int, int);  //定义一个函数指针
        int a, b, c;
        p = Max;  //把函数Max赋给指针变量p, 使p指向Max函数
        printf("please enter a and b:");
        scanf("%d%d", &a, &b);
        c = (*p)(a, b);  //通过函数指针调用Max函数
        printf("a = %d\nb = %d\nmax = %d\n", a, b, c);
        return 0;
    }
    int Max(int x, int y)  //定义Max函数
    {
        int z;
        if (x > y)
        {
            z = x;
        }
        else
        {
            z = y;
        }
        return z;
    }
    

    特别注意的是,因为函数名本身就可以表示该函数地址(指针),因此在获取函数指针时,可以直接用函数名,也可以取函数的地址。

    p = Max可以改成 p = &Max
    c = (*p)(a, b) 可以改成 c = p(a, b)
    

    3.函数指针作为某个函数的参数

    既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。

    示例:

    #include <stdio.h>
    #include <stdlib.h>
    
    typedef void(*FunType)(int);
    //前加一个typedef关键字,这样就定义一个名为FunType函数指针类型,而不是一个FunType变量。
    //形式同 typedef int* PINT;
    void myFun(int x);
    void hisFun(int x);
    void herFun(int x);
    void callFun(FunType fp,int x);
    int main()
    {
        callFun(myFun,100);//传入函数指针常量,作为回调函数
        callFun(hisFun,200);
        callFun(herFun,300);
    
        return 0;
    }
    
    void callFun(FunType fp,int x)
    {
        fp(x);//通过fp的指针执行传递进来的函数,注意fp所指的函数有一个参数
    }
    
    void myFun(int x)
    {
        printf("myFun: %d\n",x);
    }
    void hisFun(int x)
    {
        printf("hisFun: %d\n",x);
    }
    void herFun(int x)
    {
        printf("herFun: %d\n",x);
    }
    

    输出:

    img

    4.函数指针作为函数返回类型

    有了上面的基础,要写出返回类型为函数指针的函数应该不难了,下面这个例子就是返回类型为函数指针的函数:

    void (* func5(int, int, float ))(int, int)
    {
        ...
    }
    

    在这里, func5(int, int, float) 为参数,其返回类型为 void (\*)(int, int) 。在C语言中,变量或者函数的声明也是一个大学问,想要了解更多关于声明的话题,可以参考我之前的文章 - C专家编程》读书笔记(1-3章)。这本书的第三章花了整整一章的内容来讲解如何读懂C语言的声明。

    5.函数指针数组

    在开始讲解回调函数前,最后介绍一下函数指针数组。既然函数指针也是指针,那我们就可以用数组来存放函数指针。下面我们看一个函数指针数组的例子:

    /* 方法1 */
    void (*func_array_1[5])(int, int, float);
    
    /* 方法2 */
    typedef void (*p_func_array)(int, int, float);
    p_func_array func_array_2[5];
    

    上面两种方法都可以用来定义函数指针数组,它们定义了一个元素个数为5,类型是 void (\*)(int, int, float) 的函数指针数组。

    6.函数指针总结

    1. 函数指针常量 :Max;函数指针变量:p;
    2. 数名调用如果都得如(*myFun)(10)这样,那书写与读起来都是不方便和不习惯的。所以C语言的设计者们才会设计成又可允许myFun(10)这种形式地调用(这样方便多了,并与数学中的函数形式一样)。
    3. 函数指针变量也可以存入一个数组内。数组的声明方法:int (*fArray[10]) ( int );

    二、回调函数

    1.什么是回调函数

    我们先来看看百度百科是如何定义回调函数的:

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    这段话比较长,也比较绕口。下面我通过一幅图来说明什么是回调:

    回调机制

    假设我们要使用一个排序函数来对数组进行排序,那么在主程序(Main program)中,我们先通过库,选择一个库排序函数(Library function)。但排序算法有很多,有冒泡排序,选择排序,快速排序,归并排序。同时,我们也可能需要对特殊的对象进行排序,比如特定的结构体等。库函数会根据我们的需要选择一种排序算法,然后调用实现该算法的函数来完成排序工作。这个被调用的排序函数就是回调函数(Callback function)。

    结合这幅图和上面对回调函数的解释,我们可以发现,要实现回调函数,最关键的一点就是要将函数的指针传递给一个函数(上图中是库函数),然后这个函数就可以通过这个指针来调用回调函数了。注意,回调函数并不是C语言特有的,几乎任何语言都有回调函数。在C语言中,我们通过使用函数指针来实现回调函数。

    我的理解是:把一段可执行的代码像参数传递那样传给其他代码,而这段代码会在某个时刻被调用执行,这就叫做回调

    如果代码立即被执行就称为同步回调,如果过后再执行,则称之为异步回调

    回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。

    回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

    2 为什么要用回调函数?

    因为可以把调用者与被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。

    简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。

    img

    int Callback()    ///< 回调函数
    {
        // TODO
        return 0;
    }
    int main()     ///<  主函数
    {
        // TODO
        Library(Callback);  ///< 库函数通过函数指针进行回调
        // TODO
        return 0;
    }
    

    回调似乎只是函数间的调用,和普通函数调用没啥区别。

    但仔细看,可以发现两者之间的一个关键的不同:在回调中,主程序把回调函数像参数一样传入库函数。

    这样一来,只要我们改变传进库函数的参数,就可以实现不同的功能,这样有没有觉得很灵活?并且当库函数很复杂或者不可见的时候利用回调函数就显得十分优秀。

    3 怎么使用回调函数?

    int Callback_1(int a)   ///< 回调函数1
    {
        printf("Hello, this is Callback_1: a = %d ", a);
        return 0;
    }
    
    int Callback_2(int b)  ///< 回调函数2
    {
        printf("Hello, this is Callback_2: b = %d ", b);
        return 0;
    }
    
    int Callback_3(int c)   ///< 回调函数3
    {
        printf("Hello, this is Callback_3: c = %d ", c);
        return 0;
    }
    
    int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
    {
        Callback(x);
    }
    
    int main()
    {
        Handle(4, Callback_1);
        Handle(5, Callback_2);
        Handle(6, Callback_3);
        return 0;
    }
    

    如上述代码:可以看到,Handle()函数里面的参数是一个指针,在main()函数里调用Handle()函数的时候,给它传入了函数Callback_1()/Callback_2()/Callback_3()的函数名,这时候的函数名就是对应函数的指针,也就是说,回调函数其实就是函数指针的一种用法。

    4.下面是一个四则运算的简单回调函数例子:

    #include <stdio.h>
    #include <stdlib.h>
    
    /****************************************
     * 函数指针结构体
     ***************************************/
    typedef struct _OP {
        float (*p_add)(float, float); 
        float (*p_sub)(float, float); 
        float (*p_mul)(float, float); 
        float (*p_div)(float, float); 
    } OP; 
    
    /****************************************
     * 加减乘除函数
     ***************************************/
    float ADD(float a, float b) 
    {
        return a + b;
    }
    
    float SUB(float a, float b) 
    {
        return a - b;
    }
    
    float MUL(float a, float b) 
    {
        return a * b;
    }
    
    float DIV(float a, float b) 
    {
        return a / b;
    }
    
    /****************************************
     * 初始化函数指针
     ***************************************/
    void init_op(OP *op)
    {
        op->p_add = ADD;
        op->p_sub = SUB;
        op->p_mul = &MUL;
        op->p_div = &DIV;
    }
    
    /****************************************
     * 库函数
     ***************************************/
    float add_sub_mul_div(float a, float b, float (*op_func)(float, float))
    {
        return (*op_func)(a, b);
    }
    
    int main(int argc, char *argv[]) 
    {
        OP *op = (OP *)malloc(sizeof(OP)); 
        init_op(op);
        
        /* 直接使用函数指针调用函数 */ 
        printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", (op->p_add)(1.3, 2.2), (*op->p_sub)(1.3, 2.2), 
                (op->p_mul)(1.3, 2.2), (*op->p_div)(1.3, 2.2));
         
        /* 调用回调函数 */ 
        printf("ADD = %f, SUB = %f, MUL = %f, DIV = %f\n", 
                add_sub_mul_div(1.3, 2.2, ADD), 
                add_sub_mul_div(1.3, 2.2, SUB), 
                add_sub_mul_div(1.3, 2.2, MUL), 
                add_sub_mul_div(1.3, 2.2, DIV));
    
        return 0; 
    }
    

    5. 回调函数实例(很有用)

    一个GPRS模块联网的小项目,使用过的同学大概知道2G、4G、NB等模块要想实现无线联网功能都需要经历模块上电初始化、注册网络、查询网络信息质量、连接服务器等步骤,这里的的例子就是,利用一个状态机函数(根据不同状态依次调用不同实现方法的函数),通过回调函数的方式依次调用不同的函数,实现模块联网功能,如下:

    /*********  工作状态处理  *********/
    typedef struct
    {
     uint8_t mStatus;
     uint8_t (* Funtion)(void); //函数指针的形式
    } M26_WorkStatus_TypeDef;  //M26的工作状态集合调用函数
    
    
    /**********************************************
    ** >M26工作状态集合函数
    ***********************************************/
    M26_WorkStatus_TypeDef M26_WorkStatus_Tab[] =
    {    
        {GPRS_NETWORK_CLOSE,  M26_PWRKEY_Off  }, //模块关机
        {GPRS_NETWORK_OPEN,  M26_PWRKEY_On  }, //模块开机
        {GPRS_NETWORK_Start,   M26_Work_Init  }, //管脚初始化
        {GPRS_NETWORK_CONF,  M26_NET_Config  }, /AT指令配置
        {GPRS_NETWORK_LINK_CTC,  M26_LINK_CTC  }, //连接调度中心  
        {GPRS_NETWORK_WAIT_CTC, M26_WAIT_CTC  },  //等待调度中心回复 
        {GPRS_NETWORK_LINK_FEM, M26_LINK_FEM  }, //连接前置机
        {GPRS_NETWORK_WAIT_FEM, M26_WAIT_FEM  }, //等待前置机回复
        {GPRS_NETWORK_COMM,  M26_COMM   }, //正常工作    
        {GPRS_NETWORK_WAIT_Sig,  M26_WAIT_Sig  },  //等待信号回复
        {GPRS_NETWORK_GetSignal,  M26_GetSignal  }, //获取信号值
        {GPRS_NETWORK_RESTART,  M26_RESET   }, //模块重启
    }
    /**********************************************
    ** >M26模块工作状态机,依次调用里面的12个函数   
    ***********************************************/
    uint8_t M26_WorkStatus_Call(uint8_t Start)
    {
        uint8_t i = 0;
        for(i = 0; i < 12; i++)
        {
            if(Start == M26_WorkStatus_Tab[i].mStatus)
            {          
          return M26_WorkStatus_Tab[i].Funtion();
            }
        }
        return 0;
    }
    

    所以,如果有人想做个NB模块联网项目,可以copy上面的框架,只需要修改回调函数内部的具体实现,或者增加、减少回调函数,就可以很简洁快速的实现模块联网。

    展开全文
  • 浅谈C++回调函数

    千次阅读 2018-02-28 22:09:30
     回调函数就是个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数回调函数不是由该函数的实现方直接调用,而是在...

      1.什么是回调函数?

           回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

          简单总结:什么是回调函数?——就是由声明函数的类来调用的函数叫做回调函数。普通函数可以让任何类调用。“回调”的主语是谁?——声明“回调函数”的那个类。Block、委托、通知、回调函数,它们虽然名字不一样,但是原理都一样,都是“回调机制”的思想的具体实现!

    2.回调函数的机制

          (1)定义回调函数原型;(2)提供函数实现的一方在初始化的时候,将回调函数注册给调用者;(3)当特定的事件或条件发生的时候,调用者使用函数指针调用函数对事件进行处理。

    3.回调函数的意义

           因为可以把调用者和被调用者分开,所以调用者不关心谁是被调用者。它只需知道存在一个具有特定原型和限制条件的被调用函数。简而言之,回调函数就是允许用户把需要调用的方法的指针作为参数传递给一个函数,以便该函数在处理相似事件的时候可以灵活的使用不同的方法。

    4.回调函数的作用

           想知道回调函数在实际中有什么作用?先假设有这样一种情况:我们要编写一个库,它提供了某些排序算法的实现(如冒泡排序、快速排序、shell排序、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑;或者,能让库可用于多种数据类型(int、float、string),此时,该怎么办呢?可以使用函数指针,并进行回调。

           回调可用于通知机制。例如,有时要在A程序中设置一个计时器,每到一定时间,A程序会得到相应的通知,但通知机制的实现者对A程序一无所知。那么,就需一个具有特定原型的函数指针进行回调,通知A程序事件已经发生。实际上,API使用一个回调函数SetTimer()来通知计时器。如果没有提供回调函数,它还会把一个消息发往程序的消息队列。

            再比如,假设有A、B两个类。
    (1)A类有多种形态,要在B类中实现回调函数。如假设A类是网络请求开源类ASIHttpRequest,它可能请求成功,也可能请求失败。这个时候,B类就要针对以上两个情况,作不同的处理。
    (2)A类的形态由B类决定时,要在B类中实现回调函数。如UITableView类就会提供很多回调函数(iOS专业术语称“委托”方法)

    (3)A类需要向B类传递数据时,可以在B类中实现回调函数(A类一般是数据层比较耗时的操作类)。如举的那个发工资的例子。在实际编程中,这样的机制有个好处就是可以提升用户的操作体验。比如用户从X页面跳转到Y页面,需要向网络请求数据,而且比较耗时,那我们怎么办?有三种方案:第一种就是在X页面展示一个旋转指示器,当收到网络传回的数据时,在展现Y页面。第二种就是使用回调函数。用户从X页面直接跳转到Y页面,Y页面需要到数据让数据层去执行,当收到数据时,再在Y页面展现。第三种就是在Y页面中开启多线程。让一个子线程专门到后台去取数据。综合来说,第二种更加简介易懂,而且代码紧凑。

             不管怎么说,回调函数是继承自C语言的。在C++中,应只在与C代码建立接口或与已有的回调接口打交道时,才使用回调函数。除了上述情况,在C++中应使用虚方法或仿函数(functor),而不是回调函数。

    5.代码示例

            

    typedef int(__stdcall*CompareFunction)(constbyte*,constbyte*)

         它就是回调函数的类型,负责用同样的参数形式将参数传递给相应的具体元素比较函数。另外,通过它,两个不同的排序算法,可以调用和具体元素相关的比较函数,实现和元素类型无关的排序:Bubblesort()和Quicksort(),这两个方法都用同样的参数原型,但实现了不同的排序算法。

    void DLLDIR__stdcallBubblesort(byte*array,intsize,intelem_size,CompareFunctioncmpFunc);
    void DLLDIR__stdcallQuicksort(byte*array,intsize,intelem_size,CompareFunctioncmpFunc);
    这两个函数接受以下参数:
    ·byte * array:指向元素数组的指针(任意类型)。
    ·int size:数组中元素的个数。
    ·int elem_size:数组中一个元素的大小,以字节为单位。

    ·CompareFunction cmpFunc:带有上述原型的指向回调函数的指针。

    这两个函数都会对数组进行某种排序,但每次都需决定两个元素哪个排在前面,而函数中有一个回调函数,其地址是作为一个参数传递进来的。对编写者来说,不必介意函数在何处实现,或它怎样被实现的,所需在意的只是两个用于比较的元素的地址,并返回以下的某个值(库的编写者和使用者都必须遵守这个约定):
    ·-1:如果第一个元素较小,那它在已排序好的数组中,应该排在第二个元素前面。
    ·0:如果两个元素相等,那么它们的相对位置并不重要,在已排序好的数组中,谁在前面都无所谓。
    ·1:如果第一个元素较大,那在已排序好的数组中,它应该排第二个元素后面。

    基于以上约定,函数Bubblesort()的实现如下,Quicksort()就稍微复杂一点:

    void DLLDIR__stdcall Bubblesort(byte*array,intsize,intelem_size,cmpFunc)
    {
    for(inti=0;i<size;i++)
    {
    for(intj=0;j<size-i-1;j++)
    {
    //回调比较函数
    if(1==(*cmpFunc)(array+j*elem_size,array+(j+1)*elem_size))
    {
    //两个相比较的元素相交换
    byte* temp=newbyte[elem_size];
    memcpy(temp,array+j*elem_size,elem_size);
    memcpy(array+j*elem_size,array+(j+1)*elem_size,elem_size);
    memcpy(array+(j+1)*elem_size,temp,elem_size);
    delete[]temp;
    }
    }
    }
    }
    注意:因为实现中使用了memcpy(),所以函数在使用的数据类型方面,会有所局限。

    对使用者来说,必须有一个回调函数,其地址要传递给Bubblesort()函数。下面有二个简单的示例,一个比较两个整数,而另一个比较两个字符串:

    int__stdcall CompareInts(constbyte*velem1,constbyte*velem2)
    {
    int elem1=*(int*)velem1;
    int elem2=*(int*)velem2;
    if(elem1<elem2)
    return-1;
    if(elem1>elem2)
    return1;
    return0;
    }
    int __stdcall CompareStrings(constbyte*velem1,constbyte*velem2)
    {
    const char* elem1=(char*)velem1;
    const char* elem2=(char*)velem2;
    return strcmp(elem1,elem2);
    }

    下面另有一个程序,用于测试以上所有的代码,它传递了一个有5个元素的数组给Bubblesort()和Quicksort(),同时还传递了一个指向回调函数的指针。(使用byte类型需包含头文件windows.h,或:

    typedef unsignedchar bute;
    int main(intargc,char*argv[])
    {
    int i;
    int array[]={5432,4321,3210,2109,1098};
    cout<<"BeforesortingintswithBubblesort\n";
    for(i=0;i<5;i++)
    cout<<array[i]<<'\n';
    Bubblesort((byte*)array,5,sizeof(array[0]),&CompareInts);
    cout<<"Afterthesorting\n";
    for(i=0;i<5;i++)
    cout<<array[i]<<'\n';
    const char str[5][10]={"estella","danielle","crissy","bo","angie"};
    cout<<"BeforesortingstringswithQuicksort\n";
    for(i=0;i<5;i++)
    cout<<str[i]<<'\n';
    Quicksort((byte*)str,5,10,&CompareStrings);
    cout<<"Afterthesorting\n";
    for(i=0;i<5;i++)
    cout<<str[i]<<'\n';
    return0;
    }
    如果想进行降序排序(大元素在先),就只需修改回调函数的代码,或使用另一个回调函数,这样编程起来灵活性就比较大了。

    上面的代码中,可在函数原型中找到__stdcall,因为它以双下划线打头,所以它是一个特定于编译器的扩展,说到底也就是微软的实现。任何支持开发基于Win32的程序都必须支持这个扩展或其等价物。以__stdcall标识的函数使用了标准调用约定,为什么叫标准约定呢,因为所有的Win32 API(除了个别接受可变参数的除外)都使用它。标准调用约定的函数在它们返回到调用者之前,都会从堆栈中移除掉参数,这也是Pascal的标准约定。但在C/C++中,调用约定是调用者负责清理堆栈,而不是被调用函数;为强制函数使用C/C++调用约定,可使用__cdecl。另外,可变参数函数也使用C/C++调用约定。
    Windows操作系统采用了标准调用约定(Pascal约定),因为其可减小代码的体积。这点对早期的Windows来说非常重要,因为那时它运行在只有640KB内存的电脑上。

    如果你不喜欢__stdcall,还可以使用CALLBACK宏,它定义在windef.h中:

    #define CALLBACK__stdcallor
    #define CALLBACKPASCAL//而PASCAL在此被#defined成__stdcall
    作为回调函数的C++方法

    因为平时很可能会使用到C++编写代码,也许会想到把回调函数写成类中的一个方法,但先来看看以下的代码:

    class CCallbackTester
    {
    public:
    int CALLBACKCompareInts(constbyte*velem1,constbyte*velem2);
    };
    Bubblesort((byte*)array,5,sizeof(array[0]),&CCallbackTester::CompareInts);
    如果使用微软的编译器,将会得到下面这个编译错误:

    errorC2664:’Bubblesort’:cannotconvertparameter4from’int(__stdcallCCallbackTester::*)(constunsignedchar*,constunsigne)

    这是因为非静态成员函数有一个额外的参数:this指针,这将迫使你在成员函数前面加上static。

    6.使用回调函数的好处

    (1)可以让实现方,根据回调方的多种形态进行不同的处理和操作。(ASIHttpRequest)
    (2)可以让实现方,根据自己的需要定制回调方的不同形态。(UITableView)
    (3)可以将耗时的操作隐藏在回调方,不影响实现方其它信息的展示。
    (4)让代码的逻辑更加集中,更加易读。


    展开全文
  • 回调函数C++11

    千次阅读 2018-12-10 22:05:27
    回调函数就是个通过函数指针调用的函数。我们是通过这个函数指针来调用其指向的函数,这就是我们说的回调机制(Callback)。 为什么使用回调机制(Callback) 原因:使用回调机制可以编写与被调用函数中的逻辑无关...
  • C语言 回调函数详解

    千次阅读 2020-03-03 09:14:04
    但是回调函数的理解和使用却不是件简单的事,在本篇我们根据我们个人的理解和应用经验对回调函数做简要的分析。 1、什么是回调函数  既然谈到了回调函数,首先我们就要搞清楚什么是回调函数。在讨论回调函数...
  • 一文搞懂C语言回调函数

    万次阅读 多人点赞 2019-07-09 00:40:58
    博主:Rdou Typing 来源:segmentfault 什么是回调函数 ...回调函数就是个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另个函数,当这个指针被用来调用其所指向的函数时,我...
  • java回调函数(全干货)

    千次阅读 2021-06-26 17:00:59
    接口回调的意义是通过接口来实现解耦的的前提下调用另个类的方法,也就是B为A准备个callback回调方法,然后B把任务丢给A,A做完任务然后通过调用B的方法callback #以下的解释如果不精通设计模式就不易理解,请...
  • C++ bind与回调函数

    千次阅读 2020-04-20 23:35:00
    函数1,2 为个模块,为回调函数,函数3为个模块,为注册回调函数,形参为函数指针 注册回调函数的入参为函数指针,指向回调函数回调函数实际就是指针类型的实例化 与类无关的回调函数 void basketball()//...
  • 函数指针 10.1.1 函数类型 通过什么来区分两个不同的函数函数在编译时被分配个入口地址,这个...这一点和数一样,因此我们可以用个指针变量来存放这个入口地址,然后通过该指针变量调用函数。 注意:通
  • 【深度学习框架Keras】Keras回调函数

    千次阅读 2018-11-08 13:11:25
    实现自定义回调函数的方法是继承keras.callbacks.Callback类,如何重写下面这些方法,这些方法会在不同时间点被调用。 这些方法都有个logs参数,这个参数是个字典,里面包含了前(轮、batch或次训练)的...
  • python在回调函数中获取返回值的方法python中有用到回调函数的时候,而回调函数又需要返回数值的时候,就需要先将所被传为回调函数的函数先赋值给个变量,然后等回调结束之后,将这个变量取值回来就可以了。...
  • 回调函数的作用

    万次阅读 多人点赞 2017-03-12 09:02:09
    回调函数的作用 原文地址:http://wmnmtm.blog.163.com/blog/static/3824571420105484116877/ 一直不太理解回调函数的作用,下面是找到的一些关于回调函数的作用的解答。...2.回调函数就相当于个中断处
  • lambda表达式与回调函数

    千次阅读 2019-01-18 14:31:19
    C++ 11中使用lambda表达式实现回调 ...C++使用模板、函数指针、接口和lambda表达式这四种方法做回调函数的区别比较 https://www.cnblogs.com/kanite/p/8299147.html   C/C++回调方式系列之 函数指针...
  • opencv 之 鼠标事件,回调函数

    千次阅读 2018-04-17 09:54:08
    主要介绍 鼠标事件的函数原型及参数 回调函数的原型及参数 鼠标事件画矩形的例子指定鼠标操作消息回调函数的函数为SetMouseCallback函数。函数原型: void SetMouseCallback(const string &amp; winname,...
  • 实现效果为,ESP8266作为TCP client端,具有服务器掉线重连功能,每隔2秒往服务器发送个255以内的随机数,同时接受服务器控制指令,如果为“on”则打开ESP8266上的灯,为“off”则关灯,其它指令不动作
  • JNA(Java Native Access)框架是个开源的Java框架,是SUN公司主导开发的,建立在经典的JNI的基础之上的个框架。非常强大、易用。其中JNA是对JNI的封装,能让java使用者更好的使用本地的动态库 、JNA与JNI的...
  • web前端之回调函数(callback)

    千次阅读 2021-02-02 20:09:57
    回调函数一组在训练的特定阶段被调用的函数集,你可以使用回调函数来观察训练过程中网络内部的状态和统计信息。通过传递回调函数列表到模型中,即可在给定的训练阶段调用该函数集中的函数 <script> ...
  • 闭包,回调函数,CallbackProperty
  • 回调函数(附例子)适合菜鸟小白

    万次阅读 多人点赞 2016-06-29 14:41:21
    转载来自... 看了人家的例子,比看那么多定义好多了。看就明白了。...非把个简单的东西复杂化。...回调函数:我的理解。假设 A是回调函数,B是调用者,
  • 那么,回调函数是个什么鬼呢?它和函数到底有何异同?既然已经有了函数,为啥还非要生出个回调函数来?想必,小伙伴们在刚碰到这个概念的时候,都会被这些问题困扰。网上搜搜,有很多相关的材料,但是未必透彻。我...
  • 数据回调函数优先级 II . 数据回调函数 相关内容 III . 采样率 处理细节 IV . 数据回调函数 每次 采样个数 numFrames V . 数据回调函数 缓冲区 ( AAudio 内部缓冲区 ) 调整 VI . AAudio 音频系统的线程安全性分析
  • useEffect可以用作componentWillUnmount(),需要写在回调函数里,并返回,比如取消计时器如下: const [count, setCount] = React.useState(0)//返回个数组,第个是状态state function unmount() { ReactDOM....
  • 1.概念  在编写程序的时候,我们通常会在主函数中调用库里的各种函数,这个过程叫做调用,函数在调用的时候通常需要...这时候的库函数就调用了你编写的函数,这个过程就叫做回调。  这时被库函数调用的函数,也就是
  • js函数回调

    万次阅读 多人点赞 2019-07-31 19:09:41
    平常的前端开发工作中,编写js时会有很多地方用到函数回调。 最简单的例子就是: <script language="javascript" type="text/javascript"> function doSomething(callback) { if(typeof callback == ...
  • 就像自定义数据类型一样,我们也可以先定义函数指针类型,然后再用这个类型来申明函数指针变量。 我先给你个自定义数据类型的例子。 typedef int* PINT; //为int* 类型定义了个PINT的别名 int main
  • 回调函数(函数指针的应用)

    千次阅读 2016-05-03 19:41:15
    # 本篇博客主要阐述回调函数,函数指针的概念,并且使用回调函数实现冒泡排序,可以 实现不同数据类型的排序。...回调函数就是个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另 个函数,
  • Callback的本质是设置函数指针进去,然后在需要需要触发某个事件时调用该方法, 比如Windows的窗口消息处理函数就是这种类型。比如下面的示例代码,我们在Download完成时需要触发个通知外面的事件:#include ...
  • tensorflow回调函数

    千次阅读 2019-01-08 13:39:35
    回调函数一组在训练的特定阶段被调用的函数集,你可以使用回调函数来观察训练过程中网络内部的状态和统计信息。通过传递回调函数列表到模型的.fit()中,即可在给定的训练阶段调用该函数集中的函数。 【Tips】虽然...
  • 回调函数使用方法--例子程序

    千次阅读 2016-11-13 19:53:15
    假设 A是回调函数,B是调用者,B参数里个是指向A的函数指针,即回调A,同时另外的参数传递给A作为参数。A可以是多个函数的统一指向,只要函数参数个数相同即可。 WINDOWS回调函数:永远不会被程序中的其他函数...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 256,793
精华内容 102,717
热门标签
关键字:

一组回调函数