C++:函数指针进阶:看完还不用std::function来捶我

news/2024/5/20 6:03:09/文章来源:https://blog.csdn.net/u013620306/article/details/128041316

1:函数指针的背景

我们先简单聊一下函数指针的背景,具体使用请参考我的这篇博客

C++ :函数: 函数指针_hongwen_yul的博客-CSDN博客

假设现在有这样一段代码:C/C++中可以使用指针指向一段代码,这个指针就叫函数指针,假设有这样一段代码、

// main.cpp
#include<iostream>int func(int a) {return a+1;
}int main(){int (*f)(int) = func;printf("%p",f);
}// 打印结果 :400526

我们定义一个函数 func() ,然后使用指针变量 f 指向该函数,然后打印该变量f 指向的地址,代码是很简单的,然后我们编译一下,看下编译后生成的指令,接下来我们重点关注fuc 函数:

 

 可以看到,编译好的函数 func 位于地址 ox400526,然后我们运行下编译好的程序,发现输出的数据和编译后的地址是一致的,这个指针变量保存了函数func 的机器指令。

所以说明:函数指针本质上也是一个指针,只不过这个指针指向的不是内存中的一段数据,而是内存中的一段代码。

 有的同学可能会有疑问,:为什么编译器在生成可执行文件时就知道函数 func 存放在内存地址 0x400526上了?这不是应该是程序被加载到内存后开始运行时,才能确定的吗?

答案就在函数指针:可以把一段代码当做一个变量传来传去,这个变量保存了这段代码地址,它的主要用途之一就是 回调函数,关于回调函数,你可以参考我的这篇文章。

C++:函数:回调函数:还不懂回调函数来捶我_hongwen_yul的博客-CSDN博客q​​​​​​回调函数------函数指针https://blog.csdn.net/u013620306/article/details/128009205?spm=1001.2014.3001.5501

其实回调函数就是这样的

 

 

2:函数指针的衍生

上面我们说到,可以定义一个指针变量来保存函数地址,但是了,假如我们我们的场景在复杂点:

我们依然需要在模块A中定义函数,同时运行模块A 需要依赖模块B产生的数据,然后将模块A定义的函数和模块B产生的数据一并传递给模块C来调用,就像这样:

 很显然单纯的函数指针已经不够用了,因为函数指针只是单纯的指向了内存中的一段代码,但是我们不仅需要将内存的一段代码同时也需要内存中的一块数据,一并传递给模块C。

那么如何实现上面的功能了? 你可以定义一个结构体,将代码和数据打包起来、

typedef void (*func) (int);

struct closure{
  func f;
  int arg;    
};

我们将这个结构体命名为 :closure 这个结构体中包含两部分

  1. 一个指向代码的指针变量
  2. 一个保存数据的变量 

这样,我们在模块A 为指针变量赋值,在模块B为保存数据的变量赋值,然后将此结构体传递给模块C ,模块C中可以这样使用。

void run(struct functor func) {
    func->f(func->arg);
}

closure既包含了一段代码也包含了这段代码使用的数据,这里的数据称为context (函数运行依赖的数据),这个技术就是:Functor 。(就是说:单纯的函数指针并没有捕捉上下文的能力,这里的上下文就是指代码依赖的数据,你不得不自己动手构造一个结构体用来存储代码依赖的上下文。)

 有的同学又会问了:为什么不用函数指针来指向对象的成员函数,这是因为函数指针没有办法捕捉 this (指向对象的指向) 这个上下文 。 (原因我后面在讲,这个不是本文的重点)

3:std::Function

  1. 所以 std::function的作用本质和我们刚刚定义的结构体区别不大。
  2. 利用std::function你不但可以保存一段代码,同时也可以保存必要的上下文,然后在合适的地方基于上下文调用这段代码。
  3. 同时std::function 更加通用,你可以用其存储任何可以被调用的对象(callable object)只要有正确的函数签名就可以了。

好像还是不能理解?没关系,我们一步一步带领大家去理解,大家先对 std::Function有一个概念性的认识。

3.1:先想一想:C++怎样定义指向对象成员函数的指针

参考我的这篇博文

C++:函数指针进阶二:指向对象成员函数的指针_hongwen_yul的博客-CSDN博客

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

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

相关文章

【外卖项目实战开发一】

文章目录1、软件开发整体介绍2、外卖项目介绍3、环境搭建数据库环境搭建创建数据库执行SQL脚本数据表maven项目搭建添加依赖:application.yml配置application启动类4、后台登录功能开发需求分析代码开发5、后台退出功能开发1、软件开发整体介绍 软件开发流程 角色分工 软件…

Quasar搭建教程初体验

文章目录一、Quasar框架介绍二、搭建一个简单的Quasar程序1、Quasar CLI安装2、创建Quasar应用程序3、运行Quasar三、使用Quasar开发SSR四、使用Quasar开发桌面应用(Electron)1、添加Quasar Electron模式2、运行开发五、使用Quasar移动应用(Capacitor)1、下载安装AndroidStudio…

Scrapy基本概念——Item Pipeline

一、Item Pipeline介绍 蜘蛛抓取的每一个Item都会被发送到Item Pipeline。根据ITEM_PIPELINES的优先级设置&#xff0c;不同的Item Pipeline依次处理每一个Item&#xff0c;最后可删除该Item不做处理&#xff0c;也可将该Item发送到下一个Item Pipeline。Item Pipeline的主要用…

【Shell 脚本速成】05、Shell 运算详解

目录 一、赋值运算 二、算术运算[四则运算] 2.1 运算符与命令 2.2 整形运算 expr 命令&#xff1a;只能做整数运算&#xff0c;格式比较古板&#xff0c;运算符号两边注意空格 let命令&#xff1a;只能做整数运算&#xff0c;且运算元素必须是变量&#xff0c;无法直接对…

11.14-11.21

1.线性蒙皮 1.1 线性蒙皮定义 线性蒙皮&#xff1a;是由一系列骨骼驱动的。每个顶点会根据顶点权重图和相应的骨骼关联。根据骨骼在当前位置相对于静止位置的变换矩阵以及此顶点相对于该骨骼的权重&#xff0c;我们可以计算出该顶点在该骨骼影响下的位置。 假设Wij是第j个骨…

OpenGL原理与实践——核心模式(五):颜色、基础光照、Phong模型、材质与光

目录 颜色相关理论 什么是颜色 如何计算颜色&#xff1f; 简单实现 Phong光照模型——局部光照模型 环境光 ​编辑 漫反射 镜面反射 材质与光 材质与纹理的关系 材质在shader的体现 材质属性与光属性 光在shader的体现 整体源码实现及渲染结果 关键代码 shade…

【Kubernetes | Pod 系列】Pod的 YAML 清单文件详解

目录3. Pod的 YAML 清单文件3.1 获取资源对象 YAML3.2 解析 YAML 清单文件&#xff08;1&#xff09;apiVersion查看 Kubernetes API 中全部的 API 组&#xff08;2&#xff09;kind查看 Kubernetes 中全部的对象资源类型&#xff08;3&#xff09;metadata&#xff08;4&#…

cron表达式,结构、字段说明、特殊字符说明、常用表达式

1.cron表达式的结构 Cron表达式是一个字符串&#xff0c;结构非常简单。Cron表达式从左到右分为6或7个字段&#xff0c;每个字段代表一个含义&#xff0c;用空格隔开。如下图所示&#xff1a; 2.cron表达式中各个字段的说明和规则 Cron一共有7位&#xff0c;最后一位是年份&…

C语言既然可以自动为变量分配内存,为什么还要用动态分配内存呢?

一、前言 不知道大家在学习C语言动态分配内存的时候有没有过这样的疑问&#xff0c;既然系统可以自动帮我们分配内存&#xff0c;为什么还需要我们程序员自己去分配内存呢&#xff1f; 如果想要弄清楚这些问题&#xff0c;我们首先就要了解静态内存和动态内存有什么区别&…

Pycharm开发环境下创建python运行的虚拟环境(自动执行安装依赖包)

问题&#xff1a;基于Django开发的后台程序涉及到很多依赖的开发包&#xff0c;将该项目迁移到其它电脑环境下运行需要搭建环境&#xff0c;由于项目中有requirement.txt&#xff0c;该文件内包含了运行该项目所需的依赖&#xff1b;最简便的方式是执行命令自动安装requirement…

【App自动化测试】(九)移动端复杂测试环境模拟——来电、短信、网络切换

目录1. 发送短信2. GSM电话3. 设置模拟信号强弱4. 设置网速5. 设置网络连接类型前言&#xff1a; 本文为在霍格沃兹测试开发学社中学习到的一些技术写出来分享给大家&#xff0c;希望有志同道合的小伙伴可以一起交流技术&#xff0c;一起进步~ &#x1f618; 当我们使用模拟器来…

CUDA——向量化内存

许多 CUDA 内核受带宽限制&#xff0c;新硬件中触发器与带宽的比率增加导致更多带宽受限内核。 这使得采取措施缓解代码中的带宽瓶颈变得非常重要。 在本文中&#xff0c;我将向您展示如何在 CUDA C/C 中使用矢量加载和存储来帮助提高带宽利用率&#xff0c;同时减少执行指令的…

kafka消息的序列化与反序列化

一、前言 在使用kafka发送消息时&#xff0c;producer端需要序列化&#xff0c;在大多数场景中&#xff0c; 需要传输的是与业务规则相关的复杂类型&#xff0c; 这就需要自定义数据结构。 Avro是一种序列化框架&#xff0c; 使用JSON来定义schema&#xff0c; sh cema由原始类…

PGL图学习之图神经网络ERNIESage、UniMP进阶模型[系列八]

PGL图学习之图神经网络ERNIESage、UniMP进阶模型[系列八] 原项目链接&#xff1a;fork一下即可&#xff1a;https://aistudio.baidu.com/aistudio/projectdetail/5096910?contributionType1 相关项目参考&#xff1a;&#xff08;其余图神经网络相关项目见主页&#xff09; …

留学生怎么正确应对Paper写作?

留学生很多都是对Paper比较抗拒的&#xff0c;因为Paper写作的频率是很高的&#xff0c;平时的课程也是比较紧凑的。相信对于初到英国留学的小伙伴们而言Paper将很长时间都是大家的痛。多少留学生被Paper折磨&#xff0c;其实英文Paper写不好的原因在于&#xff0c;对于中国留学…

单目标应用:最有价值球员算法(Most Valuable Player Algorithm,MVPA)求解旅行商问题TSP

一、最有价值球员算法 最有价值球员算法&#xff08;Most Valuable Player Algorithm&#xff0c;MVPA&#xff09;由Bouchekara 等人于2017年提出&#xff0c;该算法受到体育比赛的启发&#xff0c;球员们为了赢得冠军而组成队伍进行队伍竞争&#xff0c;他们也为了赢得最有价…

美食杰项目(二)首页

目录前言具体样式代码思路加载样式相应组件相应代码总结&#xff1a;前言 本节给大家讲的是美食杰项目的首页的主要功能和具体样式 具体样式 代码思路 1.点击首页跳转到首页页面 2.在父组件将轮播图所需的图片请求出来&#xff0c;再传给轮播组件 3.在父组件将商品列表的数据…

阿里云服务器采用AMD CPU处理器ECS实例规格详解

阿里云服务器有AMD CPU处理器&#xff0c;阿里云服务器ECS通用型g7a、计算型c7a和内存型r7a采用2.55 GHz主频的AMD EPYCTM MILAN处理器&#xff0c;单核睿频最高3.5 GHz&#xff1b;通用型g6a、计算型c6a和内存型r6a采用2.6 GHz主频的AMD EPYCTM ROME处理器&#xff0c;睿频3.3…

Contos7中Mysql忘记密码或者初始登录时密码错误解决方法

Contos7中Mysql忘记密码或者初始登录时密码错误解决方法 1、先停止mysql服务 service mysqld stop2、修改mysql的配置文件&#xff0c;通过下面命令&#xff0c;进入配置文件当中 vim /etc/my.cnf在文件末尾加上 skip-grant-tables3、重启mysql服务&#xff0c; service m…

Websocket学习

参考&#xff1a;http://www.mydlq.club/article/86/ 这里写目录标题一、WebSocket 简介二、WebSocket 特点三、为什么需要 WebSocket四、WebSocket 连接流程五、WebSocket 使用场景六、使用案例1.提醒客户端有新订单2.客户端交互一、WebSocket 简介 WebSocket 是一种基于 TCP…