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

【C++】C++11 ~ 包装器解析

🌈欢迎来到C++专栏~~包装器解析


  • (꒪ꇴ꒪(꒪ꇴ꒪ )🐣,我是Scort
  • 目前状态:大三非科班啃C++中
  • 🌍博客主页:张小姐的猫~江湖背景
  • 快上车🚘,握好方向盘跟我有一起打天下嘞!
  • 送给自己的一句鸡汤🤔:
  • 🔥真正的大师永远怀着一颗学徒的心
  • 作者水平很有限,如果发现错误,可在评论区指正,感谢🙏
  • 🎉🎉欢迎持续关注!
    在这里插入图片描述

请添加图片描述

文章目录

  • 🌈欢迎来到C++专栏~~包装器解析
    • 🥑 概念
    • 🥑类型统一
    • ✨例题:求解逆波兰表达式
    • ✨包装器的意义
      • bind 包装器😎
      • 💥bind 绑定固定参数
      • 💥调整传参顺序
      • 💥bind包装器的意义
  • 📢写在最后

请添加图片描述

🥑 概念

function包装器 也叫作适配器。C++中的function本质是一个类模板,也是一个包装器。
那么我们来看看,我们为什么需要function呢?

包装器定义式:

// 类模板原型如下
template <class T> function;     // undefined
template <class Ret, class... Args>
class function<Ret(Args...)>;

模板参数说明:

  • Ret: 是被包装的可调用对象的返回值类型
  • Args... :是被包装的可调用对象的形参类型

function包装器可以对可调用对象进行包装,包括函数指针、函数名、仿函数(函数对象)、lambda表达式

int f(int a, int b)
{return a + b;
}struct Functor
{
public:int operator() (int a, int b){return a + b;}
};class Plus
{
public://静态 vs 非静态static int plusi(int a, int b){return a + b;}double plusd(double a, double b){return a + b;}
};int main()
{function<int(int, int)> f1 = f;f1(1, 2);function<int(int, int)> f2 = Functor();f2(1, 2);function<int(int, int)> f3 = Plus::plusi;f3(1, 2);//非静态成员函数    要 + 对象function<double(Plus, double, double)> f4 = &Plus::plusd;f4(Plus(), 1.1, 2.2);return 0;
}

注意事项:

  • 取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“&
  • 非静态成员函数在调用的时候要 + 对象,因为非静态成员函数的第一个参数是隐藏this指针,所以在包装时需要指明第一个形参的类型为类的类型。

🥑类型统一

对于以下函数模板useF:

  • 传入该函数模板的第一个参数可以是任意的可调用对象,比如函数指针、仿函数、lambda表达式等
  • useF中定义了静态变量count,并在每次调用时将count的值和地址进行了打印,可判断多次调用时调用的是否是同一个useF函数。

代码如下:

template<class F, class T>
T useF(F f, T x)
{static int count = 0;cout << "count:" << ++count << endl;cout << "count:" << &count << endl;return f(x);
}

在不使用包装器,直接传入对象的时候,会实例化出三份

double f(double i)
{return i / 2;
}
struct Functor
{double operator()(double d){return d / 3;}
};
int main()
{//函数指针cout << useF(f, 11.11) << endl;//仿函数cout << useF(Functor(), 11.11) << endl;//lambda表达式cout << useF([](double d)->double{return d / 4; }, 11.11) << endl;return 0;
}

输出结果如下:
在这里插入图片描述

由于函数指针、仿函数、lambda表达式是不同的类型,因此useF函数会被实例化出三份,三次调用useF函数所打印count的地址也是不同的。

  • 但实际这里根本没有必要实例化出三份useF函数,因为三次调用useF函数时传入的可调用对象虽然是不同类型的,但这三个可调用对象的返回值和形参类型都是相同的
  • 这时就可以用包装器分别对着三个可调用对象进行包装,然后再用这三个包装后的可调用对象来调用useF函数,这时就只会实例化出一份useF函数
  • 根本原因就是因为包装后,这三个可调用对象都是相同的function类型,因此最终只会实例化出一份useF函数,该函数的第一个模板参数的类型就是function类型的

包装后代码如下:

int main()
{// 函数指针function<double(double)> f1 = f;cout << useF(f1, 11.11) << endl;// 函数对象function<double(double)> f2 = Functor();cout << useF(f2, 11.11) << endl;// lamber表达式function<double(double)> f3 = [](double d)->double { return d / 4; };cout << useF(f3, 11.11) << endl;return 0;
}

在这里插入图片描述

✨例题:求解逆波兰表达式

题目地址:传送

解题思路:

  1. 首先定义一个栈,依次遍历所给字符串
  2. 遇到数字,直接入栈,遇到操作符,则从栈定抛出两个数字进行对应的运算,并将运算后得到的结果压入栈中
  3. 所给字符串遍历完毕后,栈顶的数字就是逆波兰表达式的计算结果

此处的包装器:

  • 建立各个运算符与其对应需要执行的函数之间的映射关系,当需要执行某一运算时就可以直接通过运算符找到对应的函数进行执行
  • 当运算类型增加时,就只需要建立新增运算符与其对应函数之间的映射关系即可(其他代码不用动)
class Solution {
public:int evalRPN(vector<string>& tokens) {stack<long long> st;map<string, function<long long(long long, long long)>> opfuncMap = {//自动构造pair ~ 初始化列表构造{"+", [](long long a , long long b){return a + b;}},{"-", [](long long a , long long b){return a - b;}},{"*", [](long long a , long long b){return a * b;}},{"/", [](long long a , long long b){return a / b;}},};for(auto& str : tokens){if(opfuncMap.count(str)){//操作符 :出栈(先出右,再出左)long long right = st.top();st.pop();long long left = st.top();st.pop();st.push(opfuncMap[str](left, right));}else{//操作数:入栈st.push(stoll(str));}}return st.top();}
};

✨包装器的意义

  • 将可调用对象的类型进行统一,便于我们对其进行统一化管理。
  • 包装后明确了可调用对象的返回值和形参类型,更加方便使用者使用。

bind 包装器😎

bind 是一种函数包装器,也叫做适配器。它可以接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表,C++ 中的 bind 本质还是一个函数模板

bind函数模板的原型如下:

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

模板参数说明:

  • fn:可调用对象
  • args...:要绑定的参数列表:值或占位符

调用bind的一般形式

auto newCallable = bind(callable, arg_list);

callable:需要包装的可调用对象
newCallable:生成的新的可调用对象
arg_list:逗号分隔的参数列表,对应给定的 callable 的参数,当调用 newCallable时,newCallable 会调用 callable,并传给它 arg_list 中的参数

_1 _2 ... 是定义在placeholders命名空间中,代表绑定函数对象的形参;_1代表第一个形参,_2代表第二个形参 …

举例:

using namespace placeholders;
int x = 2, y = 10;
Div(x, y);auto bindFun1 = bind(Div, _1, _2);

💥bind 绑定固定参数

原本传入的参数要求是要3个,现在只需要输入两个参数,因为绑定了固定的函数对象

using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{//function<int(Sub, int, int)> fsub = &Sub::sub;function<int(int, int)> fsub = bind(&Sub::sub, Sub(), _1, _2);
}

想把Mul函数的第三个参数固定绑定为1.5,可以在绑定时将参数列表的placeholders::_3设置为1.5。比如:

int Mul(int a, int b, int rate)
{return a * b * rate;
}int main()
{function<int(int, int)> fmul = bind(Mul, _1, _2, 1.5);
}

💥调整传参顺序

对于 Sub 类中的 sub 函数,因为 sub 的第一个参数是隐藏的 this 指针,如果想要在调用 sub 时不用对象进行调用,那么可以将 sub 的第一个参数固定绑定为一个 Sub 对象:

using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数function<int(int, int)> func = bind(&Sub::sub, Sub(), _1, _2);cout << func(1, 2) << endl; return 0;
}

此时调用对象时就只需要传入用于相减的两个参数,因为在调用时会固定帮我们传入一个匿名对象给 this 指针。

如果想要将 sub 的两个参数顺序交换,那么直接在绑定时将 _1 和_2 的位置交换一下就行了:

using namespace placeholders;class Sub
{
public:int sub(int a, int b){return a - b;}
};
int main()
{//绑定固定参数function<int(int, int)> func = bind(&Sub::sub, Sub(), _2, _1);cout << func(1, 2) << endl; return 0;
}

其原理:第一个参数会传给_1,第二个参数会传给 _2,因此可以在绑定时通过控制 _n 的位置,来控制第 n 个参数的传递位置

💥bind包装器的意义

  • 将一个函数的某些参数绑定为固定的值,让我们在调用时可以不用传递某些参数。
  • 可以对函数参数的顺序进行灵活调整。

📢写在最后

kd

请添加图片描述

相关文章:

【C++】C++11 ~ 包装器解析

&#x1f308;欢迎来到C专栏~~包装器解析 (꒪ꇴ꒪(꒪ꇴ꒪ )&#x1f423;,我是Scort目前状态&#xff1a;大三非科班啃C中&#x1f30d;博客主页&#xff1a;张小姐的猫~江湖背景快上车&#x1f698;&#xff0c;握好方向盘跟我有一起打天下嘞&#xff01;送给自己的一句鸡汤&a…...

SpringBoot整合(三)SpringBoot发送邮件

使用SpringBoot发送邮件 邮件发送其实是一个非常常见的需求&#xff0c;用户注册&#xff0c;找回密码等地方&#xff0c;都会用到&#xff0c;Spring Boot 中对于邮件发送&#xff0c;提供了相关的自动化配置类&#xff0c;使得邮件发送变得非常容易。 1、前置工作 目前国内…...

【docker知识】联合文件系统(unionFS)原理

一、说明 Docker CLI 操作起来比较简单——您只需掌握Create、Run、InspPull和Push容器和图像&#xff0c;但是谁想过Docker 背后的内部机制是如何工作的&#xff1f;在这个简单的表象背后隐藏着许多很酷的技术&#xff0c; UnionFS&#xff08;统一文件系统&#xff09;就是其…...

使用Lame库实现wav、pcm转mp3

文章目录 前言 一、Lame库是什么&#xff1f; 二、使用步骤 0.创建native项目 1.下载Lame库 2.pcm转MP3 3.wav转MP3 4、native方法如下 三、注意 总结 前言 因为使用android录音后生成的文件是wav或者pcm格式&#xff0c;项目要求最后的文件需要是mp3格式&#xff0c;于…...

c++11 标准模板(STL)(std::multimap)(三)

定义于头文件 <map> template< class Key, class T, class Compare std::less<Key>, class Allocator std::allocator<std::pair<const Key, T> > > class multimap;(1)namespace pmr { template <class Key, class T…...

【报复性赚钱】2023年5大风口行业

今天就来和大家分享一下&#xff0c;在时代的洪流下&#xff0c;普通人如何顺应大势抓住机遇&#xff01; 实现人在风口上&#xff0c;猪都会飞起来。 根据对市场的观察及各平台数据分析结果&#xff0c;结合国家政策和经济专家的分析&#xff0c;小编预测了2023年将会迎来大…...

单目相机、双目相机和RGB-D相机学习笔记(一些视频和博文网址)

目录1. 单目相机1.1 摄像头原理1.2 单目相机的标定2 双目相机2.1 双目相机定位原理2.2 双目相机的缺陷3 RGB-D相机3.1 深度相机结构光原理3.2 RGB-D相机的应用1. 单目相机 1.1 摄像头原理 视频网址&#xff1a;【全网最详细】摄像头原理分析&#xff08;约25分钟课程&#xf…...

word和wps添加mathtype选项卡

word或wps添加mathtype选项卡 前提 安装好word或wps安装好mathtype 步骤 确认word或wps具体安装位置确认word或wps位数为32位还是64位复制mathtype中的MathPage.wll文件和MathType Commands 2016.dotm文件到STARTUP位置添加受信任位置添加加载项 安装位置 通过开始页面&a…...

获取成员userID

文章目录一、简介二、获取token1、获取秘钥2、获取Token三、获取部门数据1、获取部门列表2、获取子部门ID列表3、获取单个部门详情四、获取成员信息1、读取成员2、获取部门成员3、获取部门成员详情一、简介 同步数据到企微&#xff1a; 企业如果需要从自有的系统同步通讯录到…...

DOM编程-显示网页时钟

<!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title>显示网页时钟</title> </head> <body bgcolor"antiquewhite"> <script type"text/javascrip…...

浅谈保护数据的加密策略

加密是一种将信息从可读格式转换为混乱字符串的技术。这样做可以防止数据传输中的机密数据泄露。文档、文件、消息和所有其他形式的网络通信都可以加密。加密策略和身份验证服务的结合&#xff0c;还能保障企业机密信息只对授权用户开启访问权限。常见的数据加密包括以下两种&a…...

Java中String,StringBuffer和StringBuilder

String类 我们在定义string变量时 常常写 String str "hello word"; 这样的代码,看起来和int a 0; 是一样的声明方式, 但其实两者是不同的, int 是java中定义的基本数据类型, 而String是一个类&#xff0c;是一个特殊的类&#xff0c;可以像基本数据类型一样直接赋…...

华为认证常见技术问答整理:什么是Datacom认证?

一、关于Datacom认证Q&#xff1a;什么是Datacom认证&#xff1f;A&#xff1a;Datacom&#xff0c;即DatacomCommunication的缩写&#xff0c;中文为“数据通信”&#xff0c;属于ICT技术架构认证类别&#xff08;华为认证包含ICT技术架构认证、平台与服务认证和行业ICT认证三…...

Read book Netty in action (Chapter II) (Netty Introduction)

前言 支持15W的并发客户端&#xff0c;我们应该视为理所当然的事情&#xff0c;很多公司甚至能够支撑更多&#xff0c;例如我们熟知的 BAT&#xff0c;当几年前双十一的夜晚&#xff0c;并发量是不可估计的。还有春节的时候购票的时候的并发。作为一个优秀的开发人员&#xff…...

python--route

routes是用python重新实现的Rails routes系统&#xff0c;用于将url映射到应用程序的actions &#xff0c;并反过来生成url 它也是在openstack实现restful通信的方式&#xff0c;它被用来做将 URL 映射为 App 的 action&#xff0c;以及为 App的action 产生 URL 两个重要的方法…...

java面试中被问到项目中的难点,怎么回答

java面试中被问到项目中的难点&#xff0c;怎么回答回答步骤举例说明回答步骤 回答这个问题的方法取决于你的项目的类型和难度。 但是&#xff0c;一般来说&#xff0c;你可以遵循以下步骤来回答这个问题&#xff1a; 描述你的项目&#xff1a;首先简要描述你的项目的类型和目…...

【速通版】吴恩达机器学习笔记Part1

准备速通一下吴恩达的机器学习 很快做个笔记5.2.3 监督学习 part 2_哔哩哔哩_bilibili 目录 1.概述&#xff08;P1-P3) 2.supervised learning&#xff1a;&#xff08;P4,P5) regression&#xff1a; classification 3.unsupervised learning &#xff08;P6- 1.聚类算…...

面试(九)小米C++开发一面 21.11.02

1、局部变量与全局变量的区别?可以同名嘛? 首先是作用域: 局部变量只在变量声明的代码块范围内生效 全局变量在其声明后的所有位置都能访问到 在局部变量与全局变量同名的情况下,全局变量会被屏蔽掉,只会使用局部变量的内容 2、extern 当在a.c中想要使用b.c中的函数fu…...

儿童书写台灯哪个牌子比较好?2023儿童护眼台灯分享

现在儿童的近视率高达52.7%&#xff0c;有科技水平的提高和电子产品的普及&#xff0c;近视率逐年攀升&#xff0c;出现低龄化现象&#xff0c;调查结果显示&#xff0c;其中6岁儿童达到14.3%&#xff0c;小学生为35.6%。初中生71.1%&#xff0c;高中生高达80.5%&#xff0c;可…...

市场调研计划书如何写?

想要做好一个产品&#xff0c;市场调研是必不可少的一步&#xff0c;也是第一步&#xff0c;那么如何进行市场调研呢&#xff1f;以下是我整理的一份市场调研计划书&#xff0c;希望能够帮助到大家&#xff01;&#xff01;&#xff01; 一、文档版本控制 主要记录文档的版本…...

SkyWalking 10.2.0 SWCK 配置过程

SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外&#xff0c;K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案&#xff0c;全安装在K8S群集中。 具体可参…...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

OkHttp 中实现断点续传 demo

在 OkHttp 中实现断点续传主要通过以下步骤完成&#xff0c;核心是利用 HTTP 协议的 Range 请求头指定下载范围&#xff1a; 实现原理 Range 请求头&#xff1a;向服务器请求文件的特定字节范围&#xff08;如 Range: bytes1024-&#xff09; 本地文件记录&#xff1a;保存已…...

Matlab | matlab常用命令总结

常用命令 一、 基础操作与环境二、 矩阵与数组操作(核心)三、 绘图与可视化四、 编程与控制流五、 符号计算 (Symbolic Math Toolbox)六、 文件与数据 I/O七、 常用函数类别重要提示这是一份 MATLAB 常用命令和功能的总结,涵盖了基础操作、矩阵运算、绘图、编程和文件处理等…...

【JavaWeb】Docker项目部署

引言 之前学习了Linux操作系统的常见命令&#xff0c;在Linux上安装软件&#xff0c;以及如何在Linux上部署一个单体项目&#xff0c;大多数同学都会有相同的感受&#xff0c;那就是麻烦。 核心体现在三点&#xff1a; 命令太多了&#xff0c;记不住 软件安装包名字复杂&…...

LRU 缓存机制详解与实现(Java版) + 力扣解决

&#x1f4cc; LRU 缓存机制详解与实现&#xff08;Java版&#xff09; 一、&#x1f4d6; 问题背景 在日常开发中&#xff0c;我们经常会使用 缓存&#xff08;Cache&#xff09; 来提升性能。但由于内存有限&#xff0c;缓存不可能无限增长&#xff0c;于是需要策略决定&am…...

day36-多路IO复用

一、基本概念 &#xff08;服务器多客户端模型&#xff09; 定义&#xff1a;单线程或单进程同时监测若干个文件描述符是否可以执行IO操作的能力 作用&#xff1a;应用程序通常需要处理来自多条事件流中的事件&#xff0c;比如我现在用的电脑&#xff0c;需要同时处理键盘鼠标…...

Chrome 浏览器前端与客户端双向通信实战

Chrome 前端&#xff08;即页面 JS / Web UI&#xff09;与客户端&#xff08;C 后端&#xff09;的交互机制&#xff0c;是 Chromium 架构中非常核心的一环。下面我将按常见场景&#xff0c;从通道、流程、技术栈几个角度做一套完整的分析&#xff0c;特别适合你这种在分析和改…...

Java后端检查空条件查询

通过抛出运行异常&#xff1a;throw new RuntimeException("请输入查询条件&#xff01;");BranchWarehouseServiceImpl.java // 查询试剂交易&#xff08;入库/出库&#xff09;记录Overridepublic List<BranchWarehouseTransactions> queryForReagent(Branch…...

ArcPy扩展模块的使用(3)

管理工程项目 arcpy.mp模块允许用户管理布局、地图、报表、文件夹连接、视图等工程项目。例如&#xff0c;可以更新、修复或替换图层数据源&#xff0c;修改图层的符号系统&#xff0c;甚至自动在线执行共享要托管在组织中的工程项。 以下代码展示了如何更新图层的数据源&…...