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

回调函数的概念、意义和应用场景

概念

回调函数,就是使用者自己定义一个函数,并实现函数的内容,然后把这个函数作为参数传入其它函数中,由其它函数在运行时来调用。
换句话说,函数是你实现的,但由别人的函数在运行时通过参数传递的方式调用,这就是所谓的回调函数。

为什么要用回调函数?

这是一种设计策略。我们想象一种系统实现:
在一个下载系统中,有一个文件下载模块、一个下载文件当前进度显示模块,系统要求实时显示文件的下载进度,想想很简单,在面向对象的世界里,无非是实现两个类而已。
但是问题恰恰出在这里。显示模块如何驱动下载进度条?
文件下载进度只有下载模块才知道。显示模块不知道,也不应该知道下载模块的进度,这是面向对象“高内聚、低耦合”的设计特性决定的。
解决方案很简单:
给下载模块传递一个函数指针,作为回调函数,驱动显示模块的显示进度。
在面向对象的世界中这样的例子还真不少。
造成这样问题的根源,就是面向对象的程序设计思想,要求模块独立性、高内聚低耦合等特性。

回调函数机制

  • 定义一个函数(普通函数即可);
  • 将此函数的地址注册给调用者;
  • 特定的事件或条件发生时,调用者使用函数指针调用回调函数。

不带参数的回调函数

一个标准Hello World程序如下:

int main(int argc,char* argv[]){
    printf("Hello World!\n");
    return 0;
}

将它修改成函数回调样式:

//定义回调函数
void PrintfText() {
    printf("Hello World!\n");
}

//定义实现回调函数的"调用函数"
void CallPrintfText(void (*callfuct)()){
    callfuct();
}

//在main函数中实现函数回调
int main(int argc,char* argv[]){
    CallPrintfText(PrintfText);
    return 0;
}

带参数的回调函数

//定义带参回调函数
void PrintfText(char* s) {
    printf(s);
}

//定义实现带参回调函数的"调用函数"
void CallPrintfText(void (*callfuct)(char*), char* s){
    callfuct(s);
}

//在main函数中实现带参的函数回调
int main(int argc,char* argv[]){
    CallPrintfText(PrintfText,"Hello World!\n");
    return 0;
}

应用场景

扩展库

假设有这样一种情况:
我们要编写一个库,它需要排序算法(如冒泡排序、快速排序、shell排序、shake排序等等),为了能让库更加通用,不想在函数中嵌入排序逻辑,而让使用者来实现相应的逻辑。
此时,就可以把排序算法定义成回调函数,由库来调用。

定时通知机制

例如,在A模块中设置一个计时器,定期调用B模块提供的回调函数,这就是一种通知机制。

  • 编写好一个定时器模块,到了时间就触发一次调用。
  • 同样地,提供1)回调函数定义;2)注册回调函数接口。
  • 应用者把自己需要定时处理的过程编写成一个实际函数,需要满足回调函数的定义,注册到定时器。

底层模块调用高层模块,如驱动

底层模块调用高层模块,但高层模块还没写好,或者说,底层模块根本不需要知道高层模块是怎么编写的,这个时候用回调函数是最好的。
如,驱动收到数据后,调用高层应用处理,就属于这种场景。

  • 驱动层提供1)回调函数定义,2)注册回调函数的接口。
  • 应用层1)编写回调函数的实现;2)调用驱动层的注册接口将实现的回调函数指针注册给驱动层。
  • 当驱动层收到数据后,调用所注册的回调函数,处理数据。

总结

如果一个对象关心另一个对象的状态变化,那么,给状态的变化注册回调函数,让它通知你这类状态的改变,这样,在封装了模块变化的同时,实现了模块间的协作关系,另辟独径地给对象之间进行了解耦。


http://www.kler.cn/a/396578.html

相关文章:

  • CSS Module:告别类名冲突,拥抱模块化样式(5)
  • Apache Paimon、Apache Hudi、Apache Iceberg对比分析
  • ElementUI的日期组件中禁止选择小时、分钟、秒
  • 企业如何提高招聘能力?
  • ubuntu20.04 colmap 安装2024.11最新
  • 【mysql的当前读和快照读】
  • SQL 审核在 CloudQuery 的四大场景应用
  • leetcode hot100【 LeetCode 121.买卖股票的最佳时机】java实现
  • uniapp ios app以framwork形式接入sentry
  • 使用--log-file保存pytest的运行日志
  • WP网站如何增加文章/页面的自定义模板
  • Node.Js+Knex+MySQL增删改查的简单示例(Typescript)
  • 猫狗识别之BUG汇总
  • C++编程技巧与规范-类和对象
  • conda 和 pip 的比较
  • 嵌入式面试题练习 - 2024/11/15
  • NVR小程序接入平台/设备EasyNVR多个NVR同时管理设备接入:海康NVR 3.0提示不在线如何处理?
  • C++- 基于多设计模式下的同步异步日志系统
  • 力扣 LeetCode 150. 逆波兰表达式求值(Day5:栈与队列)
  • 第 6 章 - Go 语言 运算符
  • MacOS下,如何在Safari浏览器中打开或关闭页面中的图片文字翻译功能
  • 【Python爬虫实战】轻量级爬虫利器:DrissionPage之SessionPage与WebPage模块详解
  • c++中,头文件包含iostream.h和`<iostream>`的差别
  • 【Flink】-- flink新版本发布:v2.0-preview1
  • Ubuntu24.04 network:0 unclaimed wireless adapter no found
  • DAY110代码审计-PHP框架开发篇ThinkPHP版本缺陷不安全写法路由访问利用链