• 关于回调函数


     1.什么是回调函数。

     在FreeRDP的项目中看到,几乎所有的绘制工作都是通过回调函数完成的。究竟什么是回调函数呢?要了解回调函数,还得中函数指针说起。那函数指针又是什么呢?它跟指针函数有什么关系呢?请看下面:

    一般我们是这样声明一个函数的,
    int func(int params, ...);
    相信大家对函数最熟悉不过了,这是个返回值为int的函数。稍微变形一下就成了指针函数了,请看:
    int* func(int params, ...);
    一个函数不仅可以带回一个整型数据的值,字符类型值和实型类型的值,还可以带回指针类型的数据,使其指向某个地址单元。当一个函数的返回值是一个指针时,该函数就是一个指针函数。那跟指针函数有什么关系呢?先看看指针函数的定义吧。
    在程序运行中,函数代码是程序的算法指令部分,它们和数组一样也占用存储空间,都有相应的地址。可以使用指针变量指向数组的首地址,也可以使用指针变量指向函数代码的首地址,指向函数代码首地址的指针变量称为函数指针。
    int func(int x); /* 声明一个函数 */

      int (*f) (int x); /* 声明一个函数指针 */
      f=func; /* 将func函数的首地址赋给指针f */

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

    2.为什么要使用回调函数

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

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

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

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

     3.回调函数的实现

    
    
     #include <stdio.h>
    #include <string.h>
    #include <assert.h>
    #include <math.h>
    #include <malloc.h>
    /*
    File: darray.c:回调函数冒泡排序函数实现
    Author: ecorefeng
    Created on 2010年8月
    */
    /*
    *定义一个用于自动测试的宏(关于自动测试请阅读我的博客文章《自动测试的优劣》)
    */
    #define return_val_if_fail(p, val)\
    if(!(p)){printf("%s:%d"#p" failed", func, LINE); return val; }


    /*
    *定义一个回调函数原型,用于回调
    */
    typedef int (*CompFunc)(void *ctx, void *data);
    /*
    *功能:实现冒泡排序
    *参数:array:要排序的数组 compfunc:回调的用于比较的函数 array_len:数组长度
    *返回:
    */
    int darray_b_sort(void **array,CompFunc compfunc, int array_len)
    {
    return_val_if_fail(array !=NULL&&compfunc !=NULL, 1);
    int len = 0;
    int max = 0;
    int index = 0;
    for(len = array_len - 1; len > 0; len--)
    {
    for(index = 1, max = 0; index < len; index++)
    {
    if(compfunc(array[index], array[max]) > 0)
    {
    max = index;
    }
    }
    if(compfunc(array[max], array[len]) > 0)
    {
    void *data = array[max];
    array[max] = array[len];
    array[len] = data;
    }
    //int i = 0;
    //for(i = 0; i < 6; i++)
    //{
    //printf("%d\n",array[i]);
    // assert(array[i] >= array[i-1]);
    //}
    //printf(".......................\n");
    }
    return 0;
    }
    /*
    *功能:随机生成一个数组
    *参数:num:数组长度
    *返回:数组指针
    */
    static void **int_array_create(int num)
    {
    int i = 0;
    int *array = (int *)malloc(sizeof(int) * num);
    for(i = 0; i < num; i++)
    {
    array[i] = rand()%100;
    printf("%d\t",array[i]);
    }
    printf("***********************\n");
    return(void **)array;
    }
    /*
    *功能:实现比较
    *参数:compfunc:回调的用于比较的函数 num:数组长度
    *返回:数组指针
    */
    static void sort_test_asc_or_desc(CompFunc compfunc, int num)
    {
    void **array = int_array_create(num);
    darray_b_sort(array, compfunc, num);
    int i = 0;
    for(i = 0; i < num; i++)
    {
    printf("%d\t",(int)array[i]);
    // assert(array[i] >= array[i-1]);
    }
    free(array);
    }
    /*
    *功能:实现比较函数(升序)
    *参数:指针
    *返回:比较结果:大于0、小于0;
    */
    int int_sort_compfunc_asc(void *num1, void *num2)
    {
    return (int)num1 - (int)num2;
    }
    /*
    *功能:实现比较函数(降序)
    *参数:指针
    *返回:比较结果:大于0、小于0;
    */
    int int_sort_compfunc_desc(void *num1, void *num2)
    {
    return (int)num2 - (int)num1;
    }
    int main(int argc, char *argv[])
    {
    sort_test_asc_or_desc(int_sort_compfunc_asc, 20);
    printf("................\n");
    sort_test_asc_or_desc(int_sort_compfunc_desc, 10);
    return 0;
    }


    
    

     回调函数的形式实现的升降序的冒泡排序算法例子。
    感谢ecorefeng提供的程序例子。
    回头看看FreeRDP的项目,正是由于回调函数的使用,使得FreeRDP的移植变得简单多了。例如我是用skia库实现rdp的绘制函数时,不需要了解整个的调用过程,我只需要实现回调函数调用的那些函数,然后注册这些回调函数就可以了。
    由于知识有限,难免有错,欢迎大家指正,谢谢。

  • 相关阅读:
    慎用rm -rf
    Jquery 中a||""的含义
    【学习、总结】Spring security 登陆超时处理
    Eclipse 无限编译Invoking 'Maven Project Builder'导致卡主
    For多重循环 break continue
    随机编码的生成
    QQ互联Oauth2.0认证测试
    Java开发工程师(Web方向)
    Java开发工程师(Web方向)
    前端开发工程师
  • 原文地址:https://www.cnblogs.com/hopetribe/p/2385441.html
Copyright © 2020-2023  润新知