C++学习之回调函数

参考文献

1. 什么是回调函数

回调函数就是通过函数指针调用函数。如果把函数的指针或者地址作为参数传递给另一个参数,当这个指针被用来调用其所指向的函数时,那么这就是一个回调的过程,这个被回调的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或者条件发生时由另外的一方调用的,用于对该事件或者条件进行响应。回调函数就是在两个独立函数或者独立类通信的通道。

2. 回调函数和普通函数的区别

  • 对普通函数的调用:调用程序发出对普通函数的调用后,程序执行立即转向被调用函数执行,直到被调用函数执行完毕后,再返回调用程序继续执行。从发出调用的程序的角度看,这个过程为“调用–>等待被调用函数执行完毕–>继续执行”。

  • 对回调函数调用:调用程序发出对回调函数的调用后,不等函数执行完毕,立即返回并继续执行。这样,调用程序执和被调用函数同时在执行。当被调函数执行完毕后,被调函数会反过来调用某个事先指定函数,以通知调用程序:函数调用结束。这个过程称为回调(Callback),这正是回调函数名称的由来。

两者实现上的区别:

3. 同步回调和异步回调的区别

  • 回调可以是同步也可以是异步
  • 同步可以是单线程也可以是多线程
  • 异步必须是多线程或多进程(每个进程可以是单线程) ==> 换句话说,异步必须依靠多线程或多进程才能完成

(1) 同步回调:把函数b传递给函数a。执行a的时候,回调了b,a要一直等到b执行完才能继续执行;

(2) 异步回调:把函数b传递给函数。执行a的时候,回调了b,然后a就继续往后执行,b独自执行。

4. 同步回调的实现

// C代码实现的同步回调函数
#include<stdio.h>
#include<string.h>

typedef int(*mCallback)(char *actions[], int actions_len); // 第二种回调的写法

// D函数:回调函数
int RemoteControl(char *actions[], int actions_len){
    printf("C函数被调用.    ==>  你朋友通过你开的 ***远程控制*** 操作你的电脑.\n");
    for(int i = 0; i < actions_len; i++){
        printf(">> %s\n", actions[i]);
    }
    printf("C函数返回.       ==>  处理完毕,退出远程的使用.\n");
    return 0;
}

// B函数
int YourFriend(int (*callback)(char *actions[], int actions_len)){
    // actions是提供的一些动作
    char *actions[] = {"右键计算机",
                     "选择属性",
                     "选择高级系统设置",
                     "... ..."
                    };
    printf("B函数被调用.    ==>  你朋友收到你的求助.\n");
    callback(actions, 4); // 回调函数的执行
    printf("B函数返回.      ==>  你朋友向你反馈处理结果.\n");
    return 0;
}

// C函数
int MyFriend(mCallback callback){
    // actions是提供的一些动作
    char *actions[] = {"右键计算机",
                     "选择属性",
                     "选择高级系统设置",
                     "... ..."
                    };
    printf("B函数被调用.    ==>  你朋友收到你的求助.\n");
    callback(actions, 4); // 回调函数的执行
    printf("B函数返回.      ==>  你朋友向你反馈处理结果.\n");
    return 0;
}




// A函数
int You(){
    printf("A函数启动.      ==>  你用电脑,遇到了问题.\n");
    printf("A函数调用B函数.  ==>  你向你朋友发起求助,同时提供 ***远程控制*** 给他.\n");
    /* 第一种回调的实现 */
    YourFriend(RemoteControl); 

    /* 第二种回调的实现 */
    mCallback callback(RemoteControl);
    MyFriend(callback);
    
    printf("A函数返回.    ==>  你出门办事.\n");
    return 0;
}

int main(){
    char *msg[] = {"Hello", "World"};
    You();
    return 0;
}

程序运行输出结果如下

A函数启动.      ==>  你用电脑,遇到了问题.
A函数调用B函数.  ==>  你向你朋友发起求助,同时提供 ***远程控制*** 给他.
B函数被调用.    ==>  你朋友收到你的求助.
C函数被调用.    ==>  你朋友通过你开的 ***远程控制*** 操作你的电脑.
>> 右键计算机
>> 选择属性
>> 选择高级系统设置
>> ... ...
C函数返回.       ==>  处理完毕,退出远程的使用.
B函数返回.      ==>  你朋友向你反馈处理结果.
A函数返回.    ==>  你出门办事.

一个疑问:同步回调和普通函数的调用在栈上的执行顺序有什么区别?

5. 异步回调的实现

异步回调必定是多线程或者多进程的,不可能是单线程或者单进程的。

程序实例1:

#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

/*C(回调)函数*/
int remote_control(char *actions[], int actions_len){
    printf("C函数被调用.     ==>  你朋友通过你开的 ***远程控制*** 操作你的电脑.\n");
    for(int i = 0; i < actions_len; i++){
        printf(">> %s\n", actions[i]);
    }
    printf("C函数返回.       ==>  处理完毕,退出远程的使用.\n");
    return 0;
}

/*B函数*/
int your_friend(int rc(char *actions[], int actions_len)){
    char *actions[] = {"右键计算机",
                     "选择属性",
                     "选择高级系统设置",
                     "... ..."
                    };
    printf("B函数被调用.     ==>  你朋友收到你的求助.\n");
    rc(actions, 4);
    printf("B函数返回.       ==>  你朋友向你反馈处理结果.\n");
    pthread_exit(0);
}

/*A函数*/
int you(){
    printf("A函数启动.       ==>  你用电脑,遇到了问题.\n");
    printf("A函数调用B函数.  ==>  你向你朋友发起求助,同时提供 ***远程控制*** 给他.\n");
    pthread_t id;
    int thread_ret;
    thread_ret = pthread_create(&id, NULL, (void *)your_friend, remote_control);
    if(thread_ret){
        printf("Create pthread error!");
        return -1;
    }
    pthread_detach(id);
    printf("A函数返回.       ==>  你出门办事.\n");
    //为了在标准输出看到B函数打印的信息,先不返回.
    sleep(3);
    return 0;
}

int main(void){
    char *msg[] = {"Hello", "world"};
    you();
    return 0;
}
// 另一种传参方式
#include <stdio.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>

struct callbackArgs{
    int (*callback_fun)(char *actions[], int actions_len);
} callback_args;

/*C(回调)函数*/
int remote_control(char *actions[], int actions_len){
    printf("C函数被调用.     ==>  你朋友通过你开的 ***远程控制*** 操作你的电脑.\n");
    for(int i = 0; i < actions_len; i++){
        printf(">> %s\n", actions[i]);
    }
    printf("C函数返回.       ==>  处理完毕,退出远程的使用.\n");
    return 0;
}

/*B函数*/
int your_friend(struct callbackArgs *input_function){
    char *actions[] = {"右键计算机",
                     "选择属性",
                     "选择高级系统设置",
                     "... ..."
                    };
    printf("B函数被调用.     ==>  你朋友收到你的求助.\n");
    input_function->callback_fun(actions, 4);
    printf("B函数返回.       ==>  你朋友向你反馈处理结果.\n");
    pthread_exit(0);
}

/*A函数*/
int you(){
    printf("A函数启动.       ==>  你用电脑,遇到了问题.\n");
    printf("A函数调用B函数.  ==>  你向你朋友发起求助,同时提供 ***远程控制*** 给他.\n");
    pthread_t id;
    int thread_ret;

    callback_args.callback_fun = remote_control;
    thread_ret = pthread_create(&id, NULL, (void *)your_friend, &callback_args);
    if(thread_ret){
        printf("Create pthread error!");
        return -1;
    }
    pthread_detach(id);
    printf("A函数返回.       ==>  你出门办事.\n");
    //为了在标准输出看到B函数打印的信息,先不返回.
    sleep(3);
    return 0;
}

int main(void){
    you();
    return 0;
}

上述程序中,A函数相当于应用者;C函数是A函数创建的,但是并不在A函数中执行,而是作为回调函数向底层进行注册;B函数是底层的函数。

程序输出结果:
在这里插入图片描述

程序实例2:
假设有这样一个任务,A是底层,其产生的数据要给应用者B去使用,采用异步回调机制实现如以下两端程序所示:
A.cpp

//-----------------------底层实现A-----------------------------
typedef void (*pcb)(int a); //函数指针定义,后面可以直接使用pcb,方便
typedef struct parameter{
    int a ;
    pcb callback;
}parameter; 

void* callback_thread(void *p1)//此处用的是一个线程
{
    //do something
    parameter* p = (parameter*)p1 ;
    while(1)
    {
        printf("GetCallBack print! \n");
        sleep(3);//延时3秒执行callback函数
        p->callback(p->a);//函数指针执行函数,这个函数来自于应用层B
    }
}

//留给应用层B的接口函数
extern SetCallBackFun(int a, pcb callback)
{
    printf("SetCallBackFun print! \n");
    parameter *p = malloc(sizeof(parameter)) ; 
    p->a  = 10;
    p->callback = callback;

    //创建线程
    pthread_t thing1;
    pthread_create(&thing1,NULL,callback_thread,(void *) p);
    pthread_join(thing1,NULL);
}
————————————————
版权声明:本文为CSDN博主「夏菠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiabodan/article/details/47999411

上面的代码就是底层接口程序员A写的全部代码,留出接口函数SetCallBackFun即可

下面再实现应用者B的程序,B负责调用SetCallBackFun函数,以及增加一个函数,并将此函数的函数指针通过SetCallBackFun(int a, pcb callback)的第二个参数pcb callback 传递下去。

B.cpp

//-----------------------应用者B-------------------------------
void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
{
    //do something
    printf("a = %d\n",a);
    printf("fCallBack print! \n");
}


int main(void)
{
    SetCallBackFun(4,fCallBack);

    return 0;
}
————————————————
版权声明:本文为CSDN博主「夏菠」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/xiabodan/article/details/47999411

  • 64
    点赞
  • 218
    收藏
    觉得还不错? 一键收藏
  • 6
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值