当前位置: 首页 > news >正文

C/C++面向对象(OOP)编程-回调函数详解(回调函数、C/C++异步回调、函数指针)

本文主要介绍回调函数的使用,包括函数指针、异步回调编程、主要通过详细的例子来指导在异步编程和事件编程中如何使用回调函数来实现。

🎬个人简介:一个全栈工程师的升级之路!
📋个人专栏:C/C++精进之路
🎀CSDN主页 发狂的小花
🌄人生秘诀:学习的本质就是极致重复!

目录

1 回调函数

2 C语言回调函数

2.1 函数指针

2.1.1 函数指针变量

2.1.2 定义函数类型

2.1.3 定义函数指针类型

2.2 C语言回调函数实例

2.3 C语言异步编程

3 C++回调函数

3.1 C++简单的回调实现

3.2 简答的异步回调

3.3 静态成员函数实现异步回调(有线程)


1 回调函数

        回调函数是一种特殊的函数,它作为参数传递给另一个函数,并在被调用函数执行完毕后被调用。这种机制常用于事件处理、异步编程和处理各种操作系统和框架的API。

具体来说,一个函数可以将其它的函数(也就是回调函数)作为参数传入,当特定的事件触发或者任务完成时,这个被传入的函数就自动执行。这样的设计使得代码更加模块化,增强了代码的可读性和可重用性。

        回调函数是一种设计理念,与编程语言无关,C语言中可以通过函数指针实现回调函数

        回调函数主要用于异步编程和事件处理中。

        回调函数提供了一种强大的工具,允许我们将复杂的问题分解为更小的部分进行解决,同时也增加了代码的灵活性和扩展性。

2 C语言回调函数

        C语言的回调函数是使用函数指针实现的,函数作为参数传给另一个函数,传参的函数被称为回调函数。

2.1 函数指针

2.1.1 函数指针变量

        指向函数的指针变量称为函数指针。本质上是一个指针。

        定义方式:

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

        其中,返回类型表示函数返回值的类型,参数列表表示函数接受的参数类型和个数。指针变量名就是函数指针的名称,它可以像普通变量一样被赋值和使用。

        使用实例:

  例如,定义一个指向返回值为int类型、接受两个int类型参数的函数的指针:

int (*pFunc)(int, int);

C++中可以使用new运算符动态分配内存来存储函数指针:

int (*pFunc)(int, int) = new int(*func)(int a, int b);

其中,func是一个已定义的函数,pFunc是指向该函数的指针。使用delete运算符释放内存:

delete pFunc;

2.1.2 定义函数类型

       定义方式:

        typedef 返回类型 新的函数名(参数列表);

        使用实例:

定义一个返回类型是int,参数为两个int数据,的函数类型

typedef int INT_func(int,int);

        调用方式:

int sub(int a, int b)
{return (a-b);
}// add 为指针类型,函数的地址,因此需要声明变量为指针类型
INT_func *add_p = add;
int ret = add_p(12,13);

一个例子:

#include <stdio.h>typedef int INT_func(int,int);int add(int a,int b)
{return (a+b);
}int sub(int a, int b)
{return (a-b);
}int multi(int a,int b)
{return (a*b);
}int main()
{INT_func *add_p = add;int ret = add_p(12,13);printf("12+13 = %d \n",ret);INT_func *sub_p = sub;int ret1 = sub_p(12,13);printf("12-13 = %d \n",ret1);INT_func *mutti_p = multi;int ret2 = mutti_p(12,23);printf("12*13 = %d \n",ret2);return 0;
}

        运行结果:

2.1.3 定义函数指针类型

         定义方式:

        typedef 返回类型 (*新的函数名)(参数列表);

        使用实例:

定义一个返回类型是int,参数为两个int数据,的函数类型

typedef int (*INT_func)(int,int);

        调用方式:

int sub(int a, int b)
{return (a-b);
}// add 为指针类型,函数的地址,因此需要声明变量为指针类型
INT_func  add_p = add;
int ret = add_p(12,13);

一个例子:

#include <stdio.h>typedef int (*INT_func)(int,int);int add(int a,int b)
{return (a+b);
}int sub(int a, int b)
{return (a-b);
}int multi(int a,int b)
{return (a*b);
}int main()
{INT_func add_p = add;int ret = add_p(12,13);printf("12+13 = %d \n",ret);INT_func sub_p = sub;int ret1 = sub_p(12,13);printf("12-13 = %d \n",ret1);INT_func mutti_p = multi;int ret2 = mutti_p(12,23);printf("12*13 = %d \n",ret2);return 0;
}

        运行结果:

2.2 C语言回调函数实例

        在C语言中,回调函数是一种常见的编程模式,它允许一个函数作为参数传递给另一个函数。这种特性使得我们可以将一些特定的任务委托给其他函数来完成。

#include <stdio.h>// 定义一个函数指针类型
typedef void (*Callback)(int);// 定义一个函数,接受一个回调函数作为参数
void do_something(Callback callback) {// 在这里做一些操作...printf("Doing something...\n");// 然后调用回调函数callback(42);
}// 定义一个回调函数
void my_callback(int data) {printf("Callback called with data: %d\n", data);
}int main() {// 将回调函数传递给do_something函数do_something(my_callback);return 0;
}

        在这个例子中,do_something函数接受一个Callback类型的参数,这是一个指向函数的指针。然后,do_something函数调用这个回调函数,并将一个整数作为参数传递给它。

my_callback函数是一个简单的回调函数,它接受一个整数参数并打印出来。

main函数中,我们将my_callback函数传递给do_something函数,这样当do_something函数完成其操作后,它会调用my_callback函数。

        运行结果:

2.3 C语言异步编程

        回调函数在异步编程中有着重要的作用,在接口编程应用广泛,实战中经常会注册一个回调函数来接收其他程序返回的数据,这样做的好处是调用者无需主动去获取被调用者的数据,仅仅需要回调,让被调用者去传送数据,这样就形成了回调,这是回调最常见的应用。

        假设A是视频流数据,每隔一段时间会将数据传送给B,B有两种接收方式:

        (1)A将视频数据存储好,提供接口,B根据自己的需求去调用,无需考虑时间,主动权在B。

        (2)A利用回调机制,实现回调函数,当数据来临时通知B来取数据,B注册回调函数接收数据,这样主动权在A。

        很显然第一种取数据的方式,由于B不知道数据何时来临比较低效,而第二种取数据方式A通知B来取数据,效率比较高,间接的实现了中断取数。

        这里就用到了回调,B注册一个函数,将函数地址传给A,A中调用该地址获取到B注册的函数时,我们就称这个函数为回调函数。

一个例子:

#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>// A的实现,一般会隐藏
typedef void (*CallbackPtr)(int); //函数指针定义typedef struct parameter{int a ;CallbackPtr callback;
}parameter; // 创建实例
parameter ParaData;void* callback_thread(void *p1)//此处用的是一个线程
{//do something// 循环改变p->a的值为为0 2 3 4 5 6 7 8 9 ,每隔3秒改变一次parameter* p = (parameter*)p1 ;while(1){sleep(3);//延时3秒执行callback函数p->callback(p->a);//函数指针执行函数,这个函数来自于应用层Bp->a = (p->a + 1) % 10;}
}void startup_app_A()
{// 初始化为0ParaData.a = 0;CallbackPtr init_call_back;ParaData.callback = init_call_back;//创建线程pthread_t thing1;pthread_create(&thing1,NULL,callback_thread,(void *) &ParaData);
}// 给B的接口,接收注册函数
extern void SetCallBackFun(CallbackPtr callback)
{printf("SetCallBackFun print! \n");ParaData.callback = callback;
}// //-----------------------应用者B-------------------------------
void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
{//do somethingprintf("B得到A的数据 = %d\n",a);
}int main(void)
{// 启动Astartup_app_A();SetCallBackFun(fCallBack);// 主函数while (1){// std::cout << "main function" << std::endl;printf("main function\n");sleep(2);}return 0;
}

        运行结果:

        分析结果,可以看出A提供的数据会回调B,B会每隔一定的时间收到数据,完成回调,对于一般的编程会将A的操作封装起来,只提供以下接口和函数指针类型:

typedef void (*CallbackPtr)(int);
extern void SetCallBackFun(CallbackPtr callback);

        这样B可以每次注册回调函数获取A传来的数据,如下:

void fCallBack(int a)       // 应用者增加的函数,此函数会在A中被执行
{//do somethingprintf("B得到A的数据 = %d\n",a);
}

        主函数中调用:

SetCallBackFun(fCallBack);

        这样就完成了一个C语言回调的异步编程,这在驱动领域极其常见,比如摄像头输出50fps的UVC流,程序员用UVC来完成相应的任务,这样就可以使用异步回调的方式实现。

3 C++回调函数

        C++回调函数是一种将函数作为参数传递给另一个函数的技术。在C++中,回调函数通常使用函数指针或std::function对象来实现。

        函数回调类型的定义:

typedef std::function<void(int)> Callback;

        上述定义了一个返回值为void,参数为一个int类型参数的函数回调类型Callback,等同于以下的函数指针类型:

typedef void (*Callback)(int);

3.1 C++简单的回调实现

#include <iostream>
#include <functional>// 定义一个回调函数类型
typedef std::function<void(int)> Callback;// 定义一个接受回调函数的函数
void process(int value, Callback callback) {std::cout << "处理值: " << value << std::endl;callback(value); // 调用回调函数
}// 定义一个回调函数
void myCallback(int result) {std::cout << "回调结果: " << result << std::endl;
}int main() {int value = 42;process(value, myCallback); // 传递回调函数给process函数return 0;
}

        运行结果:

3.2 简答的异步回调

#include <iostream>
#include <functional>
#include <thread>
#include <chrono>
#include <unistd.h>
void async_operation(std::function<void(int)> callback) {// 模拟一些计算std::this_thread::sleep_for(std::chrono::seconds(1));int result = 42;// 调用回调函数并将结果传递给它callback(result);
}
void callback_function(int result) {std::cout << "Async operation completed with result: " << result << std::endl;
}
int main() {// 发起异步操作,并在操作完成后调用回调函数async_operation(callback_function);// 主线程继续执行其他任务while (1){std::cout << "Main thread continues to do other work..." << std::endl;sleep(1);}return 0;
}

        运行结果:

3.3 静态成员函数实现异步回调(有线程)

        当有线程时,非静态成员函数无法实现函数的回调。需要使用静态成员函数进行回调。

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <unistd.h>
using namespace std;//--------------
// 类中定义线程,并实现回调
// A应用
template <typename Tobject, typename Tparam>
class AsynA
{public:struct ThreadParam{AsynA *pthis;Tparam a;}; // 根据线程参数自定义结构typedef void (Tobject::*Cbfun)(Tparam);public:AsynA() : number(1){printf("AsynA : AsynA()\n");}void print(void){printf("print : number = %d \n", number);}static void *funCallbackThread(void *param); void CreateCallbackThread(Tobject *pInstance, Cbfun pFun, Tparam a);void ThreadFunc(Tparam a);                   private:int number;Cbfun pCbfun;Tobject *m_pInstance;
};template <typename Tobject, typename Tparam>
void AsynA<Tobject, Tparam>::CreateCallbackThread(Tobject *pInstance, Cbfun pFun, Tparam a)
{ThreadParam *p = new ThreadParam;p->pthis = this;p->a = a;m_pInstance = pInstance;pCbfun = pFun;pthread_t thing1;pthread_create(&thing1, NULL, &funCallbackThread, (void *)p);
}template <typename Tobject, typename Tparam>
void *AsynA<Tobject, Tparam>::funCallbackThread(void *param)
{ThreadParam *p = (ThreadParam *)param;printf("ThreadParam p->a = %d\n", p->a);p->pthis->ThreadFunc(p->a);return 0;
}
template <typename Tobject, typename Tparam>
void AsynA<Tobject, Tparam>::ThreadFunc(Tparam a)
{printf("%d ThreadFunc : number = %d \n",number);while (1){const pthread_t threadID = pthread_self();sleep(a);(m_pInstance->*pCbfun)((int)threadID);}
}
// B应用
class AsynB
{public:AsynB() : number(0){}void fCallBack(int p){printf("Thread ID = %ld\n", p);}private:int number;
};int main(void)
{// 类中定义线程,并实现回调AsynA<AsynB, int> asyn_a;AsynB asyn_b;int p = 2;asyn_a.CreateCallbackThread(&asyn_b, &AsynB::fCallBack, p);AsynA<AsynB, int> asyn_a1;AsynB asyn_b1;int p1 = 4;asyn_a1.CreateCallbackThread(&asyn_b1, &AsynB::fCallBack, p1);// 主程序阻塞在这里getchar();return 0;
}

        运行结果:

        分析可知,上述代码完成在类中使用静态成员函数的异步回调,创建了两个子线程。

🌈我的分享也就到此结束啦🌈
如果我的分享也能对你有帮助,那就太好了!
若有不足,还请大家多多指正,我们一起学习交流!
📢未来的富豪们:点赞👍→收藏⭐→关注🔍,如果能评论下就太惊喜了!
感谢大家的观看和支持!最后,☺祝愿大家每天有钱赚!!!

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 腾讯云轻量应用服务器部署环境建站教程大全(2024更新)
  • C++ 编程必备:对象生命周期管理的最佳实践
  • elasticsearch列一:索引模板的使用
  • Unity 如何打造”大厅+子游戏”架构
  • SpringBoot + MyBatis-Plus 实现分页操作详解
  • 边缘计算网关在温室大棚智能控制系统应用,开启农业新篇章
  • gnu工程的编译 - 以libiconv为例
  • “a == b”和“a.equals(b)”有什么区别?
  • 谷歌Linux内核自动测试平台架构介绍-用自动测试测试难以测试的问题
  • 2024年软件测试行业展望
  • 什么是负载均衡?
  • Frappe Charts:数据可视化的强大工具
  • 如何在 Ubuntu 16.04 上使用 Minio 设置对象存储服务器
  • 鸿蒙 DevEco Studio 3.1 入门指南
  • js遍历后端返回的集合将条件相同的放入同一个数组内
  • [PHP内核探索]PHP中的哈希表
  • [数据结构]链表的实现在PHP中
  • 0基础学习移动端适配
  • Docker 1.12实践:Docker Service、Stack与分布式应用捆绑包
  • Druid 在有赞的实践
  • IDEA常用插件整理
  • JavaScript DOM 10 - 滚动
  • LeetCode18.四数之和 JavaScript
  • Node项目之评分系统(二)- 数据库设计
  • 普通函数和构造函数的区别
  • 深入浏览器事件循环的本质
  • 适配iPhoneX、iPhoneXs、iPhoneXs Max、iPhoneXr 屏幕尺寸及安全区域
  • 手写一个CommonJS打包工具(一)
  • 系统认识JavaScript正则表达式
  • 新书推荐|Windows黑客编程技术详解
  • 一份游戏开发学习路线
  • Nginx实现动静分离
  • 大数据全解:定义、价值及挑战
  • ​​​​​​​开发面试“八股文”:助力还是阻力?
  • ​RecSys 2022 | 面向人岗匹配的双向选择偏好建模
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #pragma once与条件编译
  • #我与Java虚拟机的故事#连载18:JAVA成长之路
  • $分析了六十多年间100万字的政府工作报告,我看到了这样的变迁
  • (2024.6.23)最新版MAVEN的安装和配置教程(超详细)
  • (C语言)输入自定义个数的整数,打印出最大值和最小值
  • (Matlab)基于蝙蝠算法实现电力系统经济调度
  • (笔试题)合法字符串
  • (附源码)spring boot公选课在线选课系统 毕业设计 142011
  • (附源码)springboot青少年公共卫生教育平台 毕业设计 643214
  • (免费领源码)python#django#mysql校园校园宿舍管理系统84831-计算机毕业设计项目选题推荐
  • (十二)springboot实战——SSE服务推送事件案例实现
  • (一)kafka实战——kafka源码编译启动
  • (原创)攻击方式学习之(4) - 拒绝服务(DOS/DDOS/DRDOS)
  • 、写入Shellcode到注册表上线
  • .NET C# 使用 iText 生成PDF
  • .net core 控制台应用程序读取配置文件app.config
  • .NET Core 中插件式开发实现
  • .net oracle 连接超时_Mysql连接数据库异常汇总【必收藏】
  • .NET 设计模式初探