C++-第十七章:包装器

news2025/3/1 18:58:12

目录

第一节:std::function

第二节:std::bind

        2-1.基本介绍

         2-2.调整顺序(不常用)

        2-3.调整个数

        2-4.std::bind与std::function

下期预告:


        C++中有3种可调用对象:函数指针、仿函数对象、lambda函数,经过包装器包装后屏蔽了底层细节,就可以用更加通用的方式使用3种可调用函数了。

第一节:std::function

        std::function的底层实现其实是仿函数对象,通过嵌套一层对象,实现了3种可调用对象在类型上的统一(都变成了对象)。

        std::function包含在头文件<functional>中,它的使用方法如下:

std::function<返回类型(参数类型)> 名字 = 可调用对象;
#include <functional>

// 包装函数指针
int Add(int a,int b)
{
    return a + b;
}
std::function<int(int, int)> f1 = Add;

// 包装仿函数
class Func
{
public:
    int operator()(int a, int b)
    {
        return a + b;
    }
};
std::function<int(int, int)> f2 = Func();

// 包装lambda函数
std::function<int(int, int)> f3 = [](int a, int b) {return a + b; };

int main()
{
    std::cout << f1(1, 2) << std::endl;
    std::cout << f2(1, 2) << std::endl;
    std::cout << f3(1, 2) << std::endl;
    return 0;
}

   

        如果封装的是类中的非静态成员函数,参数记得加上隐含的this指针类型,而且只有公有函数才能被封装:

class Person
{
public:
	void print()
	{
		std::cout << "我被调用了!" << std::endl;
	}
};
std::function<void(Person*)> f = &Person::print; // 这个&必不可少

        调用时也要传入一个对象的指针:

int main()
{
	Person LiHua;
	f(&LiHua); // 等价于 LiHua.print();
	return 0;
}

  

        若包装时使用Person作为参数,而不是指针,就可以直接传入对象进行调用:

#include <functional>
class Person
{
public:
	void print()
	{
		std::cout << "我被调用了!" << std::endl;
	}
};
std::function<void(Person)> f = &Person::print; // 这个&必不可少
int main()
{
	Person p;
	f(p);
	return 0;
}

  

        它们本质上都是使用对象调用成员函数。

        如果是类中的公有静态函数,则不需要隐含的this指针类型:

#include <functional>
class Person
{
public:
	static void print()
	{
		std::cout << "我被调用了!" << std::endl;
	}
};
std::function<void()> f = &Person::print; // 这个&可以省略
int main()
{
	f(); // 等价于 LiHua.print();
	return 0;
}

   

         

第二节:std::bind

        使用bind可以调整函数参数的传入顺序和个数。

        2-1.基本介绍

        std::bind的原型如下:

template<class Fn,class ...Args>
bind(Fn&& fn,Args&&... args);

template<class Ret,class Fn,class ...Args>
bind(Fn&& fn,Args&&... args);

        模版参数包就包含了可调用对象的所有参数类型。 

        std::bind的使用格式如下:

std::bind(可调用对象,占位符1,占位符2,...);

        占位符有多个,而且占位符与参数是一一对应的,占位符1表示第一个参数,占位符2表示第二个参数。

        

         2-2.调整顺序(不常用)

        因为占位符1表示第一个参数,占位符2表示第二个参数,只要把占位符1放在后面,那么第一个参数就需要在后面传入了:

#include <functional>
void func(int a, float b, std::string c)
{
	std::cout << a << std::endl;
	std::cout << b << std::endl;
	std::cout << c << std::endl;
}
int main()
{
	auto f = std::bind(&func,std::placeholders::_3, std::placeholders::_2, std::placeholders::_1);
	return 0;
}

        像这样,它的占位符顺序是3、2、1,而不是正常的1、2、3。那么使用 f 时参数类型应该先传入std::string,再传入float,最后传入int:

	f("Hello", 0.2, 12);

  

        只是顺序改变了,对原函数的逻辑没有影响。

        2-3.调整个数

        调整个数不是真正的减少参数个数,实际上是指定某个或者某些参数的值,调用时这些参数就赋值为指定的值。

        指定后,剩下的参数再重新分占位符1、2、3:

#include <functional>
void func(int a, float b, std::string c)
{
	std::cout << a << std::endl;
	std::cout << b << std::endl;
	std::cout << c << std::endl;
}
int main()
{
	// 指定第一个参数的值为12
	auto f = std::bind(&func,12, std::placeholders::_1, std::placeholders::_2);
	// 第一个参数已经预先传好了,只传剩下两个参数
	f(0.2,"Hello");
	return 0;
}

   

        这种用法的主要场景是在类中进行函数传参时,如果一个函数类型的参数只接收3个参数的可调用对象,而对象中的非静态函数有3个显式的参数+1个this指针,并且这个函数因为需要用到对象的数据而不能设置为静态,也不能减少参数。这时候将 this 作为指定为 this指针 的值,它就变成只用传入3个参数的可调用对象了。

        例如:

        它的第一个参数就被指定为 this。

        2-4.std::bind与std::function

         可调用对象被std::bind包装过后也是可调用对象,所以可以将std::bind后的可调用对象传给std::function管理起来:

#include <functional>
void func(int a, float b, std::string c)
{
	std::cout << a << std::endl;
	std::cout << b << std::endl;
	std::cout << c << std::endl;
}
int main()
{
	// 如果指定了值,那么<>中的参数也省略被指定的类型 
	std::function<void(float,std::string)> f = std::bind(&func, 12, std::placeholders::_1, std::placeholders::_2);
	// 第一个参数已经预先传好了,只传剩下两个参数
	f(0.2,"Hello");
	return 0;
}

  

下期预告:

        下一章将讲述C++11的线程库,包括线程的管理、锁。

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

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

相关文章

TCP的三次握手与四次挥手:建立与终止连接的关键步骤

引言 ‌TCP&#xff08;传输控制协议&#xff09;工作在OSI模型的传输层‌。OSI模型将计算机网络功能划分为七个层级&#xff0c;从底层到顶层依次是&#xff1a;物理层、数据链路层、网络层、传输层、会话层、表示层和应用层。传输层负责在网络节点之间提供可靠的端到端通信&a…

2025计算机考研复试资料(附:网课+历年复试真题+140所高校真题+机试)

目录 2025 计算机考研复试经验全攻略&#xff0c;附超全资源&#x1f381; &#xff08;一&#xff09;网课资源 &#xff08;二&#xff09;历年复试真题 &#xff08;三&#xff09;140 所高校真题 二、专业知识复习篇 &#xff08;一&#xff09;复试专业课程 二&…

Milvus高性能向量数据库与大模型结合

Milvus | 高性能向量数据库&#xff0c;为规模而构建Milvus 是一个为 GenAI 应用构建的开源向量数据库。使用 pip 安装&#xff0c;执行高速搜索&#xff0c;并扩展到数十亿个向量。https://milvus.io/zh Milvus 是什么&#xff1f; Milvus 是一种高性能、高扩展性的向量数据…

腾讯游戏完成架构调整 IEG新设五大产品事业部

易采游戏网2月28日独家消息&#xff1a;继1月份腾讯天美工作室群完成内部组织架构调整后&#xff0c;腾讯旗下互动娱乐事业群&#xff08;IEG&#xff09;再次宣布对组织架构进行优化调整。此次调整的核心在于新设立了五大产品事业部&#xff0c;包括体育产品部、音舞产品部、V…

达梦数据库系列之安装及Mysql数据迁移

达梦数据库系列之安装及Mysql数据迁移 1. 达梦数据库1.1 简介1.2 Docker安装达梦1.2.1 默认密码查询1.2.2 docker启动指定密码 1.3 达梦数据库连接工具1.3.1 快捷键 2 Mysql数据库迁移至达梦2.1 使用SQLark进行数据迁移 1. 达梦数据库 1.1 简介 DM8是达梦公司在总结DM系列产品…

java jar包内的jar包如何打补丁

问题描述&#xff1a; 主包&#xff1a;hisca.jar&#xff0c;解压后 BOOT-INFO/lib下有其他jar包 因为一个小bug&#xff0c;需要修改这个hisca包下BOOT-INF/lib下的子jar包service-hisca-impl-1.0.0.jar中的一个service类及xml文件 操作步骤&#xff1a; 1、主包jar -xvf …

【企业场景】上线的项目如何进行限流

一、常见的四种速率限流算法 对于限流&#xff0c;最为直接的就是速率限流了 固定窗口算法 比如 10r/s 就是把时间线分为 1s 一段&#xff0c;也就是周期为 1s&#xff0c;对一个时间段的请求进行计数&#xff0c;超过 10 则舍弃&#xff0c;未超过则直接处理经过 1s 后&…

git - study

文章目录 git - study概述可以用 git gui工具来添加快捷命令工具如果要在提交日志中搜索&#xff0c;可以用gitk的view编辑功能实验环境直接用git自带环境进行git操作的好处查看git所有配置配置全局数据配置项目专用的数据查询配置数据的原始值配置git使用的文本编辑器获取某个…

FPGA之硬件设计笔记-持续更新中

目录 1、说在前面2、FPGA硬件设计总计说明3、 原理图详解 - ARITX - 7 系列3.1 顶层框图介绍3.2 FPGA 电源sheet介绍&#xff1a;3.2.1 bank 14 和 bank 15的供电3.2.2 bank 0的供电3.2.3 Bank34 35 的供电 3.3 核电压和RAM电压以及辅助电压 4 原理图详解-- Ultrascale ARTIX4.…

嵌入式开发:傅里叶变换(5):STM32和Matlab联调验证FFT

目录 1. MATLAB获取 STM32 的原始数据 2. 将数据上传到电脑 3. MATLAB 接收数据并验证 STM32进行傅里叶代码 结果分析 STM32 和 MATLAB 联调是嵌入式开发中常见的工作流程&#xff0c;通常目的是将 STM32 采集的数据或控制信号传输到 MATLAB 中进行实时处理、分析和可视化…

Cursor AI编程-详细教程

一点准备工作 Cursor方法论&#xff1a;简单到没有方法 Cursor能做什么 Cursor官网&#xff1a;https://www.cursor.com/ja Cursor文档&#xff1a;Cursor – Welcome to Cursor Cursor论坛&#xff1a;Weekly - Cursor - Community Forum 写程序代码 举例&#xff1a; 设…

(十 三)趣学设计模式 之 模版方法模式!

目录 一、 啥是模板方法模式&#xff1f;二、 为什么要用模板方法模式&#xff1f;三、 模板方法模式的实现方式四、 模板方法模式的优缺点五、 模板方法模式的应用场景六、 总结 &#x1f31f;我的其他文章也讲解的比较有趣&#x1f601;&#xff0c;如果喜欢博主的讲解方式&a…

泛型编程、函数模板、类模板

目录 一、泛型编程 1.泛型编程提出背景 1.1.代码复用案例解析 案例1&#xff1a;实现一个交换函数&#xff0c;并对不同类型参数进行函数重载 (1)调试 (2)代码解析 ①代码复用问题 ②泛型编程的解决方案 ③上面泛型Swap函数模版的优点 1.2.泛型编程提出背景 2.泛型编…

【Vue3】浅谈setup语法糖

Vue3 的 setup 语法糖是通过 <script setup> 标签启用的特性&#xff0c;它是对 Composition API 的进一步封装&#xff0c;旨在简化组件的声明式写法&#xff0c;同时保留 Composition API 的逻辑组织能力。以下是其核心概念和原理分析&#xff1a; 一、<script setu…

简洁的个人地址发布页HTML源码

源码介绍 简洁的个人地址发布页HTML源码,源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果 效果预览 源码获取 简洁的个人地址发布页HTML源码

wav格式的音频压缩,WAV 转 MP3 VBR 体积缩减比为 13.5%、多个 MP3 格式音频合并为一个、文件夹存在则删除重建,不存在则直接建立

&#x1f947; 版权: 本文由【墨理学AI】原创首发、各位读者大大、敬请查阅、感谢三连 &#x1f389; 声明: 作为全网 AI 领域 干货最多的博主之一&#xff0c;❤️ 不负光阴不负卿 ❤️ 文章目录 问题一&#xff1a;wav格式的音频压缩为哪些格式&#xff0c;网络传输给用户播放…

Linux权限 -- 开发工具(一)

文章目录 包管理器yumyum具体操作 Linux编辑器 - vim的使用vimvim的多模式 包管理器yum Linux中安装软件&#xff1a; 1.源码安装 2. 软件包安装 – rpm 3. 包管理器yum(centos) apt/apt-get(ubuntu) 为什么有包管理器&#xff1f; 包管理器会自动帮我们解决包依赖的问题 2. 什…

【leetcode】二分查找专题

文章目录 1.二分查找1.题目2.解题思路3. 解题代码 2.在排序数组中查找元素的第一个和最后一个位置1.题目2.算法原理3. 代码 3.x的平方根1.题目2.代码 4.搜索插入位置1.题目2.解题思路3.解题代码 5.山脉数组的索引1.题目2.解题思路3. 代码 6.寻找峰值1.题目2.解题思路3.代码 7. …

《Python实战进阶》No 7: 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战

第7集&#xff1a; 一个AI大模型聊天室的构建-基于WebSocket 实时通信开发实战 在现代 Web 开发中&#xff0c;实时通信已经成为许多应用的核心需求。无论是聊天应用、股票行情推送&#xff0c;还是多人协作工具&#xff0c;WebSocket 都是实现高效实时通信的最佳选择之一。本…

vector习题

完数和盈数 题目 完数VS盈数_牛客题霸_牛客网 一个数如果恰好等于它的各因子(该数本身除外)之和&#xff0c;如&#xff1a;6321。则称其为“完数”&#xff1b;若因子之和大于该数&#xff0c;则称其为“盈数”。 求出2到60之间所有“完数”和“盈数”。 输入描述&#xff…