C/C++|std::function 浅度解析

std::function 是 C++ 标准库中的一个通用多态函数包装器。它可以存储、复制和调用任意可调用目标(函数、lambda 表达式、绑定表达式或其他函数对象)。 std::function 占有固定尺寸的内存,这是因为它的实现方式决定了这一点。让我们深入探讨这一点。

std::function 的实现原理

std::function 通常使用类型擦除和小对象优化(Small Object Optimization, SOO)来实现这一点。

  1. 类型擦除:
  • std::function 使用类型擦除来存储不同类型的可调用对象。这意味着它通过一个固定大小的存储空间和一个指向这些对象的虚表(vtable)来实现多态性。
  • 类型擦除允许 std::function 在运行时处理各种不同的类型,而不需要知道这些类型的具体细节。
  1. 小对象优化(SOO):
  • 对于小对象(通常是指尺寸较小且可以直接存储在 std::function 内部的对象),std::function 会直接在其内部存储这些对象。这避免了动态内存分配的开销。
  • 对于大对象(超出 std::function 内部存储容量的对象),std::function 会在堆上分配内存,并在内部存储一个指向这些对象的指针。

std::function 为什么占有固定尺寸的内存

由于 std::function 使用了类型擦除和小对象优化,其内部实现通常包含以下几个部分:

  • 一个指向实际存储对象的指针或存储小对象的内部缓冲区。
  • 一个指向虚表的指针,用于多态调用。
  • 一些额外的元数据,用于管理存储和调用。

这意味着,无论存储的对象是多大或多小,std::function 的实例总是占用固定大小的内存,以包含这些指针和元数据。

内存布局的示例

假设我们有以下 std::function 声明:

std::function<void()> func;

其内部可能包含如下内容:

  1. 指向可调用对象的指针 或 内部缓冲区:

    • 如果对象足够小,可以直接存储在内部缓冲区中。
    • 如果对象较大,则存储一个指向该对象的指针。
  2. 虚表指针:

    • 虚表指针指向一组函数,这些函数用于操作实际存储的对象(调用、复制、销毁等)。
  3. 元数据:

    • 用于管理存储对象的信息,如对象大小、类型信息等。

无论我们存储的是一个普通函数指针,一个小型 lambda 表达式,还是一个大型函数对象,std::function 实例的大小都是固定的

示例代码

#include <iostream>
#include <functional>void exampleFunction() {std::cout << "Hello from function!" << std::endl;
}int main() {// 存储普通函数指针std::function<void()> func1 = exampleFunction;// 存储lambda表达式std::function<void()> func2 = []() {std::cout << "Hello from lambda!" << std::endl;};// 存储大对象struct LargeFunctor {void operator()() const {std::cout << "Hello from large functor!" << std::endl;}int data[100];};std::function<void()> func3 = LargeFunctor();// 调用func1();func2();func3();std::cout << "Size of func1: " << sizeof(func1) << std::endl;std::cout << "Size of func2: " << sizeof(func2) << std::endl;std::cout << "Size of func3: " << sizeof(func3) << std::endl;return 0;
}

结果如下:

Hello from function!
Hello from lambda!
Hello from large functor!
Size of func1: 32
Size of func2: 32
Size of func3: 32

我们可以很明显得看出三个 std::function 对象大小都是一样的。

小总结:

  • std::function 的大小是固定的,因为它使用类型擦除和小对象优化来处理不同类型的可调用对象。
  • 这种固定大小的内存布局使得 std::function 可以有效地存储和管理多种不同类型的可调用对象,同时提供统一的接口来调用这些对象。
  • 无论存储的对象是函数指针、小型 lambda 表达式,还是大型函数对象,std::function 的实例大小都是固定的。

std::function 实现运行时多态

运行时多态(dynamic polymorphism)指的是在程序运行时决定调用哪个具体的函数实现。通常通过虚函数和继承来实现。std::function 通过类型擦除和虚表机制提供了类似的功能,使得它可以在运行时处理不同类型的可调用对象。

#include <iostream>
#include <functional>// 一个接受 std::function<void(int)> 类型参数的函数
void invokeWithFive(const std::function<void(int)>& func) {func(5);  // 调用传入的可调用对象,并传递参数5
}int main() {// 使用普通函数指针void (*funcPtr)(int) = [](int x) { std::cout << "Function pointer: " << x << std::endl; };invokeWithFive(funcPtr);// 使用 lambda 表达式auto lambda = [](int x) { std::cout << "Lambda: " << x << std::endl; };invokeWithFive(lambda);// 使用函数对象struct Functor {void operator()(int x) const {std::cout << "Functor: " << x << std::endl;}};Functor functor;invokeWithFive(functor);return 0;
}

在运行时,当我们调用 invokeWithFive(funcPtr)、invokeWithFive(lambda) 和 invokeWithFive(functor) 时,std::function 会根据存储的不同可调用对象类型,调用相应的实现。这种行为是运行时决定的,因此属于运行时多态。

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

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

相关文章

论文阅读:Indoor Scene Layout Estimation from a Single Image

项目地址&#xff1a;https://github.com/leVirve/lsun-room/tree/master 发表时间&#xff1a;2018 icpr 场景理解&#xff0c;在现实交互的众多方面中&#xff0c;因其在增强现实&#xff08;AR&#xff09;等应用中的相关性而得到广泛关注。场景理解可以分为几个子任务&…

C++ 内联函数 auto关键字

内联函数 用inline修饰的函数会成为内联函数&#xff0c;内联函数会在编译的阶段在调用函数的位置进行展开&#xff0c;不会涉及建立栈帧以提高效率&#xff0c;同时每一次的函数调用都会展开整个函数导致内存消耗的增加&#xff0c;是以空间换时间&#xff0c;所以内联函数比…

SpringSecurity入门(二)

8、获取用户认证信息 三种策略模式&#xff0c;调整通过修改VM options // 如果没有设置自定义的策略&#xff0c;就采用MODE_THREADLOCAL模式 public static final String MODE_THREADLOCAL "MODE_THREADLOCAL"; // 采用InheritableThreadLocal&#xff0c;它是Th…

最新下载:Navicat for MySQL 11软件安装视频教程

软件简介&#xff1a; Navicat for MySQL 是一款强大的 MySQL 数据库管理和开发工具&#xff0c;它为专业开发者提供了一套强大的足够尖端的工具&#xff0c;但对于新用户仍然易于学习。Navicat For Mysql中文网站&#xff1a;http://www.formysql.com/ Navicat for MySQL 基于…

NLP实战入门——文本分类任务(TextRNN,TextCNN,TextRNN_Att,TextRCNN,FastText,DPCNN,BERT,ERNIE)

本文参考自https://github.com/649453932/Chinese-Text-Classification-Pytorch?tabreadme-ov-file&#xff0c;https://github.com/leerumor/nlp_tutorial?tabreadme-ov-file&#xff0c;https://zhuanlan.zhihu.com/p/73176084&#xff0c;是为了进行NLP的一些典型模型的总…

如何远程桌面连接?

远程桌面连接是一种方便快捷的方式&#xff0c;可以帮助用户在不同地区的设备之间实现信息的远程通信。我们将介绍一种名为【天联】的组网产品&#xff0c;它可以帮助用户轻松实现远程桌面连接。 【天联】组网是一款异地组网内网穿透产品&#xff0c;由北京金万维科技有限公司…

绿联Nas docker 中 redis 老访问失败的排查

部署了一些服务&#xff0c;老隔3-5 天其他服务就联不上 redis 了&#xff0c;未确定具体原因&#xff0c;只记录观察到的现象 宿主机访问 只有 ipv6 绑定了&#xff0c;ipv4 绑定挂掉了 其他容器访问 也无法访问成功 当重启容器后&#xff1a; 一切又恢复正常。 可能的解…

递推8-----7-8 sdut-C语言实验-王小二切饼0)

7-8 sdut-C语言实验-王小二切饼 分数 20 全屏浏览 切换布局 作者 马新娟 单位 山东理工大学 王小二自夸刀工不错&#xff0c;有人放一张大的煎饼在砧板上&#xff0c;问他&#xff1a;“饼不许离开砧板&#xff0c;切n(1<n<100)刀最多能分成多少块&#xff1f;” 输…

浅谈一下关系型数据库中json类型字段的处理

文章目录 背景mysql json typepostgresql jsonb type场景说明mysql实验案例postgresql 实验案例 总结 背景 最近涉及到在关系型数据库中解析json数据的问题,数据库表中有一个json类型的字段,业务场景涉及到展开此单行json数据为*多行数据并和其他的表进行join查询. 以往只是知…

一个完整的java项目通常包含哪些层次(很全面)

1.View层&#xff08;视图层&#xff09; 职责&#xff1a;负责数据的展示和用户交互。在Web应用中&#xff0c;View层通常与HTML、CSS和JavaScript等技术相关。 技术实现&#xff1a;在Spring MVC中&#xff0c;View层可以使用JSP、Thymeleaf、FreeMarker等模板引擎来实现。…

MATLAB | 透明度渐变颜色条

hey 各位好久不见&#xff0c;今天提供一段有趣的小代码&#xff0c;之前刷到公众号闻道研学的一篇推送MATLAB绘图技巧 | 设置颜色条的透明度&#xff08;附完整代码&#xff09;&#xff08;https://mp.weixin.qq.com/s/bVx8AVL9jGlatja51v4H0A&#xff09;&#xff0c;文章希…

机器学习周记(第四十二周:AT-LSTM)2024.6.3~2024.6.9

目录 摘要Abstract一、文献阅读1. 题目2. abstract3. 网络架构3.1 LSTM3.2 注意力机制概述3.3 AT-LSTM3.4 数据预处理 4. 文献解读4.1 Introduction4.2 创新点4.3 实验过程4.3.1 训练参数4.3.2 数据集4.3.3 实验设置4.3.4 实验结果 5. 基于pytorch的transformer 摘要 本周阅读…

免费,C++蓝桥杯等级考试真题--第11级(含答案解析和代码)

C蓝桥杯等级考试真题--第11级 答案&#xff1a;D 解析&#xff1a; A. a b; b a; 这种方式会导致a和b最终都等于b原来的值&#xff0c;因为a的原始值在被b覆盖前没有保存。 B. swap(a&#xff0c;b); 如果没有自定义swap函数或者没有包含相应的库&#xff0c;这个选项会编…

【C++题解】1389 - 数据分析

问题&#xff1a;1389 - 数据分析 类型&#xff1a;简单循环 题目描述&#xff1a; 该方法的操作方式为&#xff0c;如果要传递 2 个数字信息给友军&#xff0c;会直接传递给友军一个整数 n&#xff08;n 是一个 10 位以内的整数&#xff09;&#xff0c;该整数的长度代表要传…

汇编语言LDS指令

在8086架构的实模式下&#xff0c;LDS指令&#xff08;Load Pointer Using DS&#xff09;用于从内存中加载一个32位的指针到指定寄存器和DS寄存器。我们来详细解释一下这条指令为什么会修改DS段寄存器。 LDS指令的功能 LDS指令格式如下&#xff1a; LDS destination, sourc…

程序猿大战Python——运算符

常见的运算符 目标&#xff1a;了解Python中常见的运算符有哪些&#xff1f; 运算符是用于执行程序代码的操作运算。常见的运算符有&#xff1a; &#xff08;1&#xff09;算术运算符&#xff1a;、-、*、/、//、% 、**&#xff1b; &#xff08;2&#xff09;赋值运算符&am…

树莓派 ubuntu linux 去除蓝牙历史配对信息

linux 去除蓝牙历史配对信息 在Linux系统中&#xff0c;蓝牙配对信息通常存储在/var/lib/bluetooth目录下的文件中。要删除所有的历史配对信息&#xff0c;你可以删除这个目录下的所有文件。 以下是一个命令行示例&#xff0c;用于删除蓝牙历史配对信息&#xff1a; sudo rm…

macOS - 终端快捷键

本文转自 Mac 上“终端”中的键盘快捷键 https://support.apple.com/zh-cn/guide/terminal/trmlshtcts/mac 以下基于系统版本 macOS Sonoma 14 文章目录 Mac 上“终端”中的键盘快捷键1、使用“终端”窗口和标签页2、编辑命令行3、在“终端”窗口中选择和查找文本4、使用标记和…

【Uniapp】uniapp微信小程序定义图片地址全局变量

错误写法&#xff1a; main.js Vue.prototype.$imgUrl 图片地址这么写之后 就发现压根不起作用&#xff1b;获取到的是undefined 正确写法&#xff1a; 返回函数&#xff0c;后面可以拼上OSS图片完整路径 Vue.prototype.$imgUrl (url) > {return ("https://地址…

[大师C语言(第二十四篇)]C语言指针探秘

引言 在C语言的学习和应用中&#xff0c;指针无疑是最重要、最难以掌握的概念之一。它为C语言提供了强大的功能和灵活性&#xff0c;同时也带来了不少的复杂性。本文将深入探讨C语言指针背后的技术&#xff0c;帮助你更好地理解和应用指针。 第一部分&#xff1a;指针的基本概…