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

👀樊梓慕:个人主页

 🎥个人专栏:《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包装器可以对函数参数的顺序进行灵活调整。

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

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

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

🌟~ 点赞收藏+关注 ~🌟

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

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

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

相关文章

# 从浅入深 学习 SpringCloud 微服务架构(二)模拟微服务环境(1)

从浅入深 学习 SpringCloud 微服务架构&#xff08;二&#xff09;模拟微服务环境&#xff08;1&#xff09; 段子手168 1、打开 idea 创建父工程 创建 artifactId 名为 spring_cloud_demo 的 maven 工程。 --> idea --> File --> New --> Project --> Ma…

【Hello算法】 > 第 3 关 >栈与队列

数据结构 之 数组与链表 1 栈 / 栈的常见操作、实现、应用2 队列 /队列的常见操作、实现、应用3 双向队列4 Tips ———————————————————————————————————————————————————————————- ————————————————…

MySQL基础篇总结

参考&#xff1a;黑马程序员MySQL基础视频链接 数据库基本操作 启动与停止 1.第一种方式&#xff1a; 1>以管理员身份运行cmd 2>在命令行窗口中输入: 启动:net start mysql80停止:net stop mysql80 2.第二种方式: 1>WinR快捷方式打开如下&#xff1a; 输入&#…

CompletableFuture编排异步线程

CompletableFuture 是 Java 8 引入的一种新的 Future&#xff0c;设计目的是为了编写非阻塞的异步代码。 传统异步编程方式 传统异步编程方式获得异步任务值&#xff0c;首先我们得通过future task &#xff0c;然后创建一个实现callable内部类&#xff0c;或者通过lambda的表…

Stable Diffusion教程:提示词(模型、插件、安装包可分享)

什么是提示词 文章提到的模型、插件、安装包都可分享&#xff0c;需要的小伙伴文末领取&#xff01; 你可能没写过提示词&#xff0c;但是一定听说过“提示词”这几个字&#xff0c;也大概能知道它的重要性。 没听说过也没关系&#xff0c;下面我就带你认识认识。 提示词就…

ARM_day6:实现字符串数据收发函数的封装

程序代码&#xff1a; uart4.h&#xff1a; #ifndef __UART4_H__ #define __UART4_H__ #include"stm32mp1xx_gpio.h" #include"stm32mp1xx_rcc.h" #include"stm32mp1xx_uart.h" void uart4_config(); void putchar(char dat); char getchar();…

GeoServer 样式指南

毫无疑问,Geoserver 是遵循 OGC 标准共享空间数据的最佳方式之一。 Geoserver 允许我们以 WMS 格式公开数据,通过以栅格(png、jpeg 等)格式显示大量矢量数据,使开发人员和用户的工作变得非常轻松。 本博客介绍了 GeoServer 功能的又一领域,即动态设置数据样式。首先,矢量…

AGM AG32 MCU在汽车UWB应用方案

AG32的汽车UWB应用方案 汽车电子产品的日益成熟&#xff0c;包括ADAS和车载信息娱乐&#xff0c;正在推动对CPLD的需求。例如&#xff0c;利用安装在车上的各种传感器&#xff08;如雷达、摄像头和激光雷达等&#xff09;来感知周围环境&#xff0c;实现实时监测和数据处理。这…

2023年网络安全行业:机遇与挑战并存

2023年全球网络安全人才概况 根据ISC2的《2023年全球网络安全人才调查报告》&#xff0c;全球的网络安全专业人才数量达到了550万&#xff0c;同比增长了8.7%。然而&#xff0c;这一年也见证了网络安全人才短缺达到了历史新高&#xff0c;缺口数量接近400万。尤其是亚太地区&am…

Centos7 的 Open Stack T 版搭建流程 --- (三)配置消息队列

配置消息队列 文章目录 配置消息队列&#xff08;1&#xff09;安装 RabbitMQ 服务并配置新用户权限controller &#xff08;2&#xff09;如何开启图形化&#xff08;拓展&#xff09; &#xff08;1&#xff09;安装 RabbitMQ 服务并配置新用户权限 controller yum install…

【支持CPU机器】一个Python文件搭建你本地图片生成编辑会话系统——TaskMatrix架构解读

一. 先上效果 TaskMatrix通过ChatGPT 和一系列 Visual Foundation 模型&#xff0c;通过聊天实现图片的绘制、问答和编辑。 二. 流程概览 1. 使用者流程 多模型会话上下文用户指令输入到本系统 -> 多模式会话基础模型 -> 理解用户指令上下文&#xff0c;调用API选择器&…

使用ssh无显示器连接树莓派并配置vnc

配置网络连接并开启ssh 使用树莓派官方的烧录工具&#xff1a; Windows下载链接 Linux下载连接 Mac下载链接 Linux还可以使用命令下载 sudo apt install rpi-imager 在烧录前点击小齿轮配置好网络连接和ssh 如果你不知道树莓派的ip地址&#xff0c;可以使用网线连接电脑和…

新品上线!这个真核宿主-宏病毒组分析流程,太合我意了吧!

病毒被称为地球的“暗物质”&#xff0c;亟待研究。近年来&#xff0c;宏病毒组&#xff08;富集/不富集&#xff09;的研究呈爆炸式增长&#xff0c;人们越来越意识到病毒在调节微生态平衡上发挥着重要作用。 对于宏病毒组项目而言&#xff0c;在新病毒的发现、病毒溯源和进化…

FastJson2中FastJsonHttpMessageConverter找不到类问题

问题描述 如果你最近也在升级FastJson到FastJson2版本&#xff0c;而跟我一样也遇到了FastJsonHttpMessageConverter找不到类问题以及FastJsonConfig找不到问题&#xff0c;那么恭喜你&#xff0c;看完本文&#xff0c;安装完fastjson2、fastjson2-extension、fastjson2-exte…

【Linux学习】Linux调试器-gdb使用

这里写目录标题 &#x1f302;背景&#x1f302;gdb使用&#x1f302;指令总结&#xff1a; &#x1f302;背景 程序的发布方式有两种&#xff0c;debug模式和 release模式 其中&#xff0c;debug模式是可以被调试的&#xff0c;到那时release模式是不能被调试的&#xff1b; …

【Python】使用Python计算简单数值积分

题外话&#xff0c;Python语言命名的来源&#xff1a;&#xff08;见下图&#xff09;Monty Python巨蟒剧团 1、积分题目&#xff08;3&#xff09; 2、解析解答 3、Python计算代码 import math import scipy.integrate as integrate# 积分区间 # x_min 0.0 # 1 # x_min …

Android14 - WindowManagerService之客户端Activity布局

Android14 - WindowManagerService之客户端Activity布局 一、主要角色 WMS作为一个服务端&#xff0c;有多种客户端与其交互的场景。我们以常见的Activity为例&#xff1a; Activity&#xff1a;在ActivityThread构建一个Activity后&#xff0c;会调用其attach方法&#xff0c;…

elementui单个输入框回车刷新整个页面

<!-- 搜索 --> <el-form :model"queryParams" ref"queryForm" :inline"true"><el-form-item label"名称" prop"nameLike"><el-input v-model"queryParams.nameLike" placeholder"请输入…

docker的安装以及docker中nginx配置

机器 test3 192.168.23.103 1机器初始化配置 1.1关闭防火墙&#xff0c;清空防火墙规则 systemctl stop firewalld iptables -F setenforce 01.2部署时间同步 yum install ntp ntpdate -y1.3安装基础软件包 yum install -y wget net-tools nfs-utils lrzsz gcc gcc-c make…

nvm管理多个node版本,快速来回切换node版本

前言 文章基于 windows环境 使用nvm安装多版本nodejs。 最近公司有的项目比较老需要降低node版本才能运行&#xff0c;由于来回进行卸载不同版本的node比较麻烦&#xff1b;所以需要使用node工程多版本管理&#xff0c;后面自己就简单捯饬了一下nvm来管理node&#xff0c;顺便…