C++ 【回调函数】详解与代码解读

在现代软件开发中,回调函数是一个常用的工具,能够实现函数调用的延迟绑定,广泛应用于事件驱动、异步操作以及模块解耦等场景。本文将从基础概念、分类、实现方式到代码示例,全面讲解 C++ 回调函数的实现和应用。


什么是回调函数?

回调函数(Callback Function)是指一个函数通过函数指针或其他机制传递给另一个函数,在执行过程中由该函数调用。这种调用方式赋予了程序更大的灵活性,因为调用方无需知道具体的函数实现,只需在需要时触发回调。

一个生活中的例子

假设你使用外卖平台(一个函数)下单,平台会在订单完成后给你打电话(回调函数)。这个电话的内容可能是你指定的(动态绑定),但外卖平台不需要了解具体的细节,只负责在指定时间触发。


C++ 回调函数的分类

根据实现方式和场景,C++ 回调函数可以分为以下几类:

  1. 普通函数的回调

    • 通过函数指针实现,是最基础的回调方式。
  2. 类成员函数的回调

    • 普通成员函数:需要对象指针和成员函数指针配合使用。
    • 静态成员函数:可作为普通函数指针使用,简单灵活。
  3. 基于 std::functionstd::bind 的回调

    • 使用 C++11 引入的标准库实现,能够兼容普通函数、成员函数和 Lambda 表达式。
  4. Lambda 表达式的回调

    • C++11 引入的匿名函数机制,适用于简洁的回调逻辑。

实现回调函数的几种方式

1. 普通函数的回调

普通函数的回调是通过函数指针来实现的。函数指针保存了一个函数的地址,调用时可以直接跳转到该函数。

#include <iostream>
using namespace std;// 普通函数,作为回调函数
void callbackFunction(int value) {// 回调逻辑cout << "普通函数回调,被调用时传入的值是: " << value << endl;
}// 一个执行回调的函数
void executeCallback(void (*callback)(int), int value) {// 通过函数指针调用回调函数callback(value);
}int main() {// 将普通函数的地址(指针)传入executeCallback(callbackFunction, 42);return 0;
}
代码解析:
  1. callbackFunction 是普通函数,定义了回调的逻辑。
  2. executeCallback 是一个高阶函数,它接收函数指针 void (*callback)(int) 作为参数,并在内部调用回调函数。
  3. main 中,将函数 callbackFunction 的地址传递给 executeCallback,实现回调。
输出:
普通函数回调,被调用时传入的值是: 42

适用场景:适合简单的场景,例如小型工具程序。


2. 类成员函数的回调

由于普通成员函数必须依赖具体对象才能调用,因此在回调时需要传递对象指针和成员函数指针。

#include <iostream>
using namespace std;class CallbackHandler {
public:// 成员函数作为回调函数void memberCallback(int value) {cout << "成员函数回调,被调用时传入的值是: " << value << endl;}
};// 一个执行回调的函数
void executeMemberCallback(CallbackHandler* obj, void (CallbackHandler::*callback)(int), int value) {// 通过对象指针和成员函数指针调用成员函数(obj->*callback)(value);
}int main() {CallbackHandler handler; // 创建一个对象// 传递对象和成员函数指针进行回调executeMemberCallback(&handler, &CallbackHandler::memberCallback, 42);return 0;
}
代码解析:
  1. CallbackHandler 类中定义了一个成员函数 memberCallback
  2. executeMemberCallback 接收对象指针 CallbackHandler* obj 和成员函数指针 void (CallbackHandler::*callback)(int),通过 (obj->*callback)(value) 调用成员函数。
  3. main 函数中,通过对象 handler 和成员函数指针 &CallbackHandler::memberCallback 实现回调。
输出:
成员函数回调,被调用时传入的值是: 42

适用场景:适用于面向对象的程序,尤其是需要调用类成员函数的情况。


3. 基于 std::functionstd::bind 的回调

std::function 是现代 C++ 中推荐的工具,可以存储任意可调用对象(普通函数、成员函数、Lambda 表达式等),而 std::bind 可以绑定成员函数与对象。

#include <iostream>
#include <functional> // 包含 std::function 和 std::bind
using namespace std;// 一个执行回调的函数
void executeCallback(std::function<void(int)> callback, int value) {// 调用回调callback(value);
}void freeFunction(int value) {cout << "普通函数回调,被调用时传入的值是: " << value << endl;
}class CallbackHandler {
public:void memberCallback(int value) {cout << "成员函数回调,被调用时传入的值是: " << value << endl;}
};int main() {CallbackHandler handler;// 1. 传递普通函数作为回调executeCallback(freeFunction, 42);// 2. 传递 Lambda 表达式作为回调executeCallback([](int value) {cout << "Lambda 回调,被调用时传入的值是: " << value << endl;}, 43);// 3. 传递成员函数作为回调executeCallback(std::bind(&CallbackHandler::memberCallback, &handler, std::placeholders::_1), 44);return 0;
}
代码解析:
  1. std::function<void(int)> 可以存储普通函数、Lambda 表达式或绑定后的成员函数。
  2. 使用 std::bind 将成员函数和对象绑定,并通过 std::placeholders::_1 占位符传递参数。
输出:
普通函数回调,被调用时传入的值是: 42
Lambda 回调,被调用时传入的值是: 43
成员函数回调,被调用时传入的值是: 44

适用场景:适合复杂场景,尤其是需要兼容不同类型回调的情况。


4. Lambda 表达式的回调

Lambda 表达式是 C++11 引入的一种简洁的回调实现方式,适合定义小型的、一次性的回调逻辑。

#include <iostream>
using namespace std;// 一个执行回调的函数
void executeCallback(auto callback, int value) {callback(value);
}int main() {// 使用 Lambda 表达式定义回调executeCallback([](int value) {cout << "Lambda 表达式回调,被调用时传入的值是: " << value << endl;}, 42);return 0;
}
代码解析:
  1. Lambda 表达式是一种匿名函数,可以直接在需要的地方定义。
  2. 使用 auto 参数,避免显式定义函数类型。
输出:
Lambda 表达式回调,被调用时传入的值是: 42

适用场景:适用于简单的回调逻辑,代码更加简洁。


总结

实现方式优点缺点适用场景
普通函数的回调简单直观灵活性不足小型工具程序
类成员函数的回调面向对象,支持类的封装调用复杂,需要传递对象和函数指针面向对象程序
std::functionstd::bind强大灵活,支持多种可调用对象性能略低于直接函数指针复杂回调场景
Lambda 表达式的回调简洁直观,代码更加紧凑不适合复杂逻辑小型回调逻辑

在实际开发中,建议优先使用 Lambda 表达式std::function,它们在现代 C++ 编程中更易读、易维护,同时具有更好的兼容性。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/891350.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式开发之使用 FileZilla 在 Windows 和 Ubuntu 之间传文件

01-FileZilla简介 FileZilla 是一个常用的文件传输工具&#xff0c;它支持多种文件传输协议&#xff0c;包括以下主要协议&#xff1a; FTP (File Transfer Protocol) 这是 FileZilla 最基本支持的协议。FTP 是一种明文传输协议&#xff0c;不加密数据&#xff08;包括用户名和…

IDEA XML 文件 SQL 提示

首先连接到对应的数据库。Database 里面要填写对应的数据库名称 配置当前项目的 SQL 方言&#xff0c;例如我这里是 MySQL 数据库管理系统&#xff0c;那么就选择 MySQL 此时就有 SQL 语法、表名、字段名等提示信息了

SSA-Transformer拿捏!麻雀搜索算法优化-Transformer多特征分类预测/故障诊断

SSA-Transformer拿捏&#xff01;麻雀搜索算法优化-Transformer多特征分类预测/故障诊断 目录 SSA-Transformer拿捏&#xff01;麻雀搜索算法优化-Transformer多特征分类预测/故障诊断效果一览基本介绍程序设计参考资料 效果一览 基本介绍 1.Matlab实现SSA-Transformer麻雀搜索…

SimForge HSF 案例分享|复杂仿真应用定制——UAVSim无人机仿真APP(技术篇)

导读 「神工坊」核心技术——「SimForge HSF高性能数值模拟引擎」支持工程计算应用的快速开发、自动并行&#xff0c;以及多域耦合、AI求解加速&#xff0c;目前已实现航发整机数值模拟等多个系统级高保真数值模拟应用落地&#xff0c;支持10亿阶、100w核心量级的高效求解。其低…

bev and occupancy 3D视觉工坊

纯激光雷达的占据预测数据集制作 Allan 方差&#xff0c;零漂&#xff0c;把imu静止在一个地方&#xff0c;看它的偏差 评估指标 waymo

ts总结一下

ts基础应用 /*** 泛型工具类型*/ interface IProps {id: string;title: string;children: number[]; } type omita Omit<IProps, id | title>; const omitaA: omita {children: [1] }; type picka Pick<IProps, id | title>; const pickaA: picka {id: ,title…

【分布式文件存储系统Minio】2024.12保姆级教程

文章目录 1.介绍1.分布式文件系统2.基本概念 2.环境搭建1.访问网址2.账号密码都是minioadmin3.创建一个桶4.**Docker安装miniomc突破7天限制**1.拉取镜像2.运行容器3.进行配置1.格式2.具体配置 4.查看桶5.给桶开放权限 3.搭建minio模块1.创建一个oss模块1.在sun-common下创建2.…

“进制转换”公式大集合

咱们都知道十进制是“逢10进1 ”&#xff0c;同理&#xff0c;N进制就是 “逢N进1”。进制其实就这么简单。它的麻烦之处在于各种进制之间的转换。 一、十进制整数转N进制 1&#xff0e;十进制转二进制 除2取余法&#xff1a;连续除以2&#xff0c;直到商为0&#xff0c;逆序…

matlab-数字滤波器设计与实战

文章目录 数字滤波器设计FIR 滤波器设计IIR 滤波器设计巴特沃斯滤波器切比雪夫 I 型滤波器切比雪夫II型椭圆滤波器线性相位与非线性相位零相位响应数字滤波器实战数字滤波器产生延迟的主要原因补偿滤波引入的延迟补偿常量滤波器延迟补偿与频率有关的延迟从信号中除去不需要的频…

python参数传递不可变对象含可变子对象

当传递不可变对象时。不可变对象里面包含的子对象是可变的。则方法内修改了这个可变对象&#xff0c;源对象也发生了变化。 a (10, 20, [5, 6]) print("a", id(a))def test01(m):print("m", id(m))m[2][0] 888print("修改m后m的值为{}".forma…

Midjourney技术浅析(八):交互与反馈

Midjourney 的用户交互与反馈通过用户输入&#xff08;User Input&#xff09;和用户反馈&#xff08;User Feedback&#xff09;机制&#xff0c;不断优化和改进图像生成的质量和用户满意度。 一、用户交互与反馈模块概述 用户交互与反馈模块的主要功能包括&#xff1a; 1.…

TB1801D 线性驱动 LED 恒流芯片

1、产品概述 TB1801D是一款专为12V灯珠设计的汽车灯专用的低压差恒流芯片&#xff0c;输出电流恒流精度≤3&#xff05;&#xff0c;外围结构简单。TB1801D 内置 130℃过温保护电路&#xff0c;可在各种散热条件下将 LED 灯珠温度控制在 140℃以内。TB1801D 内置 100V 的功率 M…

C# OpenCV机器视觉:凸包检测

在一个看似平常却又暗藏玄机的午后&#xff0c;阿强正悠闲地坐在实验室里&#xff0c;翘着二郎腿&#xff0c;哼着小曲儿&#xff0c;美滋滋地品尝着手中那杯热气腾腾的咖啡&#xff0c;仿佛整个世界都与他无关。突然&#xff0c;实验室的门 “砰” 的一声被撞开&#xff0c;小…

设计模式之访问者模式:一楼千面 各有玄机

~犬&#x1f4f0;余~ “我欲贱而贵&#xff0c;愚而智&#xff0c;贫而富&#xff0c;可乎&#xff1f; 曰&#xff1a;其唯学乎” 一、访问者模式概述 \quad 江湖中有一个传说&#xff1a;在遥远的东方&#xff0c;有一座神秘的玉楼。每当武林中人来访&#xff0c;楼中的各个房…

从0到机器视觉工程师(二):封装调用静态库和动态库

目录 静态库 编写静态库 使用静态库 方案一 方案二 动态库 编写动态库 使用动态库 方案一 方案二 方案三 总结 静态库 静态库是在编译时将库的代码合并到最终可执行程序中的库。静态库的优势是在编译时将所有代码包含在程序中&#xff0c;可以使程序独立运行&…

VisualStudio 2019 升级遇到的问题及解决

事件起因 今天计划想研究下.net core&#xff08;后面版本直接称为 .net &#xff09;,发现 .net sdk 5.0 最新版本安装不成功。解决之后&#xff0c;真是手欠&#xff0c;看着Visual Studio 2019 有更新了&#xff0c;就直接点击了&#xff0c;这时才发现问题大了。。。 安装…

Spring Boot教程之四十一:在 Spring Boot 中调用或使用外部 API

如何在 Spring Boot 中调用或使用外部 API&#xff1f; Spring Boot 建立在 Spring 之上&#xff0c;包含 Spring 的所有功能。它现在越来越受到开发人员的青睐&#xff0c;因为它是一个快速的生产就绪环境&#xff0c;使开发人员能够直接专注于逻辑&#xff0c;而不必费力配置…

HTML5实现好看的新年春节元旦网站源码

HTML5实现好看的新年春节元旦网站源码 前言一、设计来源1.1 主界面1.2 新年由来界面1.3 文章详细界面1.4 登录界面1.5 注册界面1.6 新年图册界面1.7 联系我们界面 二、效果和源码2.1 动态效果2.2 源代码 源码下载结束语 HTML5实现好看的新年春节元旦网站源码&#xff0c;春节新…

Python学习(5):数据结构

1 列表 1.1 列表方法 列表数据类型支持很多方法&#xff0c;列表对象的所有方法所示如下&#xff1a; list.append(x)&#xff1a;在列表末尾添加一项。 类似于 a[len(a):] [x]。list.extend(iterable)&#xff1a;通过添加来自 iterable 的所有项来扩展列表。 类似于 a[len…

2021.12.28基于UDP同信的相关流程

作业 1、将TCP的CS模型再敲一遍 服务器 #include <myhead.h> #define PORT 8888 #define IP "192.168.124.123" int main(int argc, const char *argv[]) {//创建套接字//绑定本机IP和端口号//监听客户端请求//接收客户端连接请求//收发消息//创建套接字int…