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

news2024/9/20 1:28:22

目录

1、function包装器

2、function包装器包装成员函数指针

2.1 静态成员函数

2.2 非静态成员函数

3、bind包装器

3.1 调整参数顺序

3.2 调整参数个数


1、function包装器

包装器是用来包装可调用对象的,这里的可调用对象主要有函数指针、仿函数、lambda表达式

function本质是一个类模板,也是一个包装器,头文件是<functional>

function<返回值(参数列表)> f = 可调用对象

int f(int a, int b)
{
	return a + b;
}
struct Functor
{
public:
	int operator() (int a, int b)
	{
		return a + b;
	}
};
int main()
{
	// 包装可调用对象
	function<int(int, int)> f1 = f;
	function<int(int, int)> f2 = Functor();
	function<int(int, int)> f3 = [](int a, int b) {return a + b; };
	cout << f1(1, 2) << endl;
	cout << f2(1, 2) << endl;
	cout << f3(1, 2) << endl;
	return 0;
}

优点是可以进行类型统一,就像我们玩游戏使用英雄释放技能时,按下一个技能的按键,英雄就会做出相应的动作,实际上,按下按键救会产生一个命令,命令通常是一个字符串,一般会将命令和函数进行映射。为了映射,此时可以使用一个map来完成映射,第一个参数存命令,第二个参数存这个命令对应的函数,但函数可能是函数指针、仿函数、lambda,lambda没有类型(底层有,但是同一个lambda在不同时间生成的类型都不一样),仿函数只能写死,函数指针可以,但太麻烦,所以就可以考虑使用包装器来进行统一

我们通过一个题目来看

150. 逆波兰表达式求值 - 力扣(LeetCode)

这道题普通的做法是开一个栈,遍历数组,当遍历到的字符是数字时,就放入栈,当遍历到的字符不是数字时,就取栈顶的两个数来进行操作,并将操作完成的数再放入栈

class Solution {
public:
    bool isNumber(const string& s)
    {
        return !(s == "+" || s == "-" || s == "*" || s == "/");
    }
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        for(const auto& e : tokens)
        {
            if(isNumber(e)) st.push(stoi(e));
            else
            {
                int y = st.top();// 注意:这里是用x / y
                st.pop();
                int x = st.top();
                st.pop();
                switch(e[0])// 这里不能是e
                {
                    case '+':
                        st.push(x + y);
                        break;
                    case '-':
                        st.push(x - y);
                        break;
                    case '*':
                        st.push(x * y);
                        break;
                    case '/':
                        st.push(x / y);
                        break;
                }
            }
        }
        return st.top();
    }
};

现在可以使用一个map,第一个参数存放操作符,第二个参数存放对应的操作

class Solution {
public:
    bool isNumber(const string& s)
    {
        return !(s == "+" || s == "-" || s == "*" || s == "/");
    }
    int evalRPN(vector<string>& tokens) {
        stack<int> st;
        map<string, function<int(int, int)>> opFuncMap = {
            {"+", [](int x, int y){return x + y;}},
            {"-", [](int x, int y){return x - y;}},
            {"*", [](int x, int y){return x * y;}},
            {"/", [](int x, int y){return x / y;}}
        };
        for(const auto& e : tokens)
        {
            if(isNumber(e)) st.push(stoi(e));
            else
            {
                int y = st.top();// 注意:这里是用x / y
                st.pop();
                int x = st.top();
                st.pop();
                int ret = opFuncMap[e](x, y);
                st.push(ret);
            }
        }
        return st.top();
    }
};

2、function包装器包装成员函数指针

2.1 静态成员函数

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<int(int, int)> f1 = Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;
	return 0;
}

静态成员函数与普通函数是类似的,只是需要指定类域

2.2 非静态成员函数

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	function<double(double, double)> f2 = Plus::plusd;
	return 0;
}

此时是错误的,非静态成员函数要取函数指针需要加&,静态成员函数可加可不加,最好也加上

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	function<double(double, double)> f2 = &Plus::plusd;
	return 0;
}

此时还是不行的,因为非静态成员函数还要隐藏的this指针,可以传指针,也可传对象(有名对象或匿名对象)

int main()
{
	// 包装静态成员函数
	function<int(int, int)> f1 = &Plus::plusi; // 要指定类域
	cout << f1(1, 1) << endl;

	// 包装非静态成员函数
	Plus ps;
	function<double(Plus*, double, double)> f2 = &Plus::plusd;
	cout << f2(&ps, 7.7, 7.7) << endl;

	// 包装非静态成员函数
	function<double(Plus, double, double)> f3 = &Plus::plusd;
	cout << f3(Plus(), 7.7, 7.7) << endl;
	return 0;
}

此时就会有一个疑问,非静态成员函数多出来的参数是this指针,是一个指针,为什么可以传对象呢?

实际上,function本质并不是将3个参数传给plusd,是拿到函数指针后,将函数指针作为成员变量存起来,我们传参时实际上是调用了function的operator(),operator()又去调用plusd,而调用plusd就需要用到Plus的对象或指针。也就是说,不是直接将3个参数传给plusd,并且this指针也不支持显示传参

3、bind包装器

bind是一个函数模板,主要用于对可调用对象调整参数的顺序、个数,返回一个仿函数,直接使用auto接收

placeholders是一个命名空间,里面有很多标识符(_1,_2,_3,...),可用这些标识符去代表对应的参数,_1始终代表第一个实参,_2代表第二个实参

3.1 调整参数顺序

using placeholders::_1;
using placeholders::_2;
using placeholders::_3;
int Sub(int x, int y)
{
	return (x - y) * 10;
}
int main()
{
	// 调整参数顺序
	auto sub1 = bind(Sub, _1, _2);
	cout << sub1(10, 5) << endl;
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;
	return 0;
}

结果是50  -50。_1代表的始终是第1个实参,无论是sub1(10, 5),还是sub2(10, 5),第1个实参都是10,都会匹配到_1,但是sub1和sub2中_1处在的位置是不同的,sub1会将_1当成第1个形参来传,sub2会将_1当成是第2个形参来传,也就是说在调用函数时,是按顺序传的

3.2 调整参数个数

int main()
{
	// 调整参数顺序
	auto sub1 = bind(Sub, _1, _2);
	cout << sub1(10, 5) << endl;
	auto sub2 = bind(Sub, _2, _1);
	cout << sub2(10, 5) << endl;

	// 调整参数个数
	auto sub3 = bind(Sub, 100, _1);
	cout << sub3(5) << endl;
	auto sub4 = bind(Sub, _1, 100);
	cout << sub4(5) << endl;
	return 0;
}

结果是950  -950

实际上,bind返回的是一个仿函数对象,也就是说上面的sub1、sub2、sub3、sub4都是仿函数对象,本质也是调用operator(),只是里面调用的顺序不同

其实,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)> f1 = bind(&Plus::plusd, Plus(), _1, _2);
	cout << f1(7.7, 7.7) << endl;
	return 0;
}

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

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

相关文章

CSS渐变设计指南

CSS渐变设计指南 背景渐变文字渐变SVG渐变 背景渐变 开发界面时&#xff0c;渐变的图像会相比固定颜色的图形更加富有层次感与有趣。使用CSS可以轻松地为文本添加渐变效果。 实现CSS背景色渐变&#xff0c;可以使用CSS3的 background-image 属性结合 linear-gradient() 函数。…

智谱清影 -CogVideoX-2b-部署与使用,带你揭秘生成6s视频的极致体验!

文章目录 1 效果展示2 CogVideoX 前世今生3 CogVideoX 部署实践流程3.1 创建丹摩实例3.2 配置环境和依赖3.3 模型与配置文件3.4 运行4 遇到问题 1 效果展示 A street artist, clad in a worn-out denim jacket and a colorful bandana, stands before a vast concrete wall in …

JavaScript基础学习:预解析机制

JavaScript基础学习&#xff1a;预解析机制 前言 在 JavaScript 的世界里&#xff0c;代码的执行并不是简单地从上到下按顺序进行的。 在实际执行之前&#xff0c;JavaScript 引擎会进行一个特殊的阶段&#xff0c;称为“预解析”。 这一阶段对于理解 JavaScript 的行为至关…

2025武汉国际半导体产业与电子技术博览会

时间&#xff1a;2025年5月14日-16日地点&#xff1a;武汉中国光谷科技会展中心 展会简介&#xff1a; 为了推动中西部地区电子信息产业的跨越式发展&#xff0c;促进先进技术在中西部地区的创新应用&#xff0c;由中国光电子发展大会组委会联合沃森展览共同打造的2025 武汉国…

网络安全 DVWA通关指南 DVWA Stored Cross Site Scripting (存储型 XSS)

DVWA Stored Cross Site Scripting (存储型 XSS) 文章目录 DVWA Stored Cross Site Scripting (存储型 XSS)XSS跨站原理存储型 LowMediumHighImpossible 参考文献 WEB 安全靶场通关指南 XSS跨站原理 当应用程序发送给浏览器的页面中包含用户提交的数据&#xff0c;但没有经过适…

Yocto - 使用Yocto开发嵌入式Linux系统_01 前言

Embedded Linux Development Using Yocto Project: Leverage the power of the Yocto Project to build efficient Linux-based products, Third Edition By: Otavio Salvador, Daiane Angolini Overview of this book Yocto 项目是开发可靠的嵌入式 Linux 项目的行业标准。与…

C++ | Leetcode C++题解之第405题数字转换为十六进制数

题目&#xff1a; 题解&#xff1a; class Solution { public:string toHex(int num) {if (num 0) {return "0";}string sb;for (int i 7; i > 0; i --) {int val (num >> (4 * i)) & 0xf;if (sb.length() > 0 || val > 0) {char digit val …

内存管理(C++版)

C/C内存分布 程序经过编译生成可执行的二进制程序&#xff0c;我们可以把虚拟进程地址分为以下四个空间&#xff1a;栈&#xff0c;堆&#xff0c;常量区&#xff0c;静态区。这四个区里面存贮的也是不一样的内容。 各个区域所存储内容的说明 栈/堆栈&#xff1a;用于建立函…

C++—string类接口与用法大总结(其中涉及STL基础)

目录 1.string类的本质 2.string类的构造 1.普通构造 2.功能型构造 1.拷贝构造功能型 2.带参构造功能型 3.其余构造 3.operator[] 4.迭代器&#xff08;iterator&#xff09; 1.概念 2.改变string对象本身 3.正向迭代器&#xff08;iterator&#xff09; 4.反向迭代…

基于springboot的驾校预约管理系统的设计与实现 (含源码+sql+视频导入教程)

&#x1f449;文末查看项目功能视频演示获取源码sql脚本视频导入教程视频 1 、功能描述 基于springboot的驾校预约管理系统5拥有三种角色&#xff1a;管理员、教练、学员 管理员&#xff1a;学员管理、教练管理、车辆管理、车辆关联、维修管理等 教练&#xff1a;学员查看/毕…

通过Python代码发送量化交易信号邮件通知

量化交易利用数学模型和计算机算法来分析市场数据,并生成交易信号,本文将介绍如何使用Python编写一个简单的脚本,通过发送邮件通知量化交易信号。 开启SMTP服务 首先要在发件箱的邮件设置中,将POP3/SMPT服务开启,记录下授权密码,在本地可通过此密码登录,注意有效期和保…

总结拓展十:SAP开发计划(下)

第一节 接口功能开发说明书设计 1、软件系统接口作用 答&#xff1a;系统接口&#xff0c;是实现系统间数据传输的功能。 2、软件系统接口特点 1&#xff09;采用Web Service技术作为平台&#xff0c;有众多的数据传输协议标准&#xff0c;通过API与外界交流数据。 2&…

向上转移和向下转型

向上转型 实际就是创建一个子类对象&#xff0c;将其当成父类对象来使用。格式&#xff1a;父类类型 对象名new 子类类型&#xff08;&#xff09;&#xff1b;eg&#xff1a;Animal animalnew Cat&#xff08;&#xff09;&#xff1b;animal是父类类型&#xff0c;但可以引用…

超详细超实用!!!零基础java开发之云风笔记接口开发之查询单条笔记详细信息(十二)

云风网 云风笔记 云风知识库 一、service/NoteApi新增getNodeDetail接口定义 public interface NoteApi {...NoteManage getNoteDetail(int id); }二、service/impl/NoteServiceImpl接口实现逻辑 public class NoteServiceImpl implements NoteApi {AutowiredNoteMapper not…

产品经理入门攻略:如何从零开始成为产品经理

“人人都是产品经理”这句话相信你一定听过。 作为现在的热门职业&#xff0c;许多朋友也在心里埋下了一颗想要成为产品经理的种子。 产品经理的工作其实没有传说中的那么“高大上”&#xff0c;甚至可以说大多数时候是枯燥且无聊的&#xff0c;需要不断地对数据进行分析&…

如何在ONLYOFFICE文档中,将新插件添加为选项卡

随着 ONLYOFFICE 文档8.1版本的推出&#xff0c;我们引入了各式各样的功能&#xff0c;旨在增强软件功能性和优化用户体验。其中一项改进是&#xff0c;插件可以显示在一个独立的选项卡中了。在本博客文章中&#xff0c;我们将带您了解充分利用这一新功能的各项步骤。 关于 ONL…

图像修复(Inpainting)技术的前沿模型与数据集资源汇总

图像修复&#xff08;Image Inpainting&#xff09;是一种计算机视觉技术&#xff0c;旨在填补图像中的缺失区域或去除图像中的不需要部分&#xff0c;使其看起来自然且无明显痕迹。其目标是根据图像的上下文信息和周围像素来推断和重建缺失区域的内容&#xff0c;以生成逼真且…

实战17-NavBar+Vip布局

NavBar.ets import { PADDING } from ../../constants/size import rvp from ../../utils/resposive/rvIndexComponent export default struct NavBar {StorageProp(topHeight) topHeight: number 0;build() {Row() {Row({ space: rvp(6) }) {Text(请选择地址).fontSize(rvp(1…

Java基础笔记1】Java基础语法

目录 一、Java简介 二、JDK和Java初体验 三、配置环境变量 四、IDEA快捷键 五、Java语法基础 1. 注释 2. 字面量 3. 变量 4. 关键字和标识符 5. 变量详解 a. 数值数据在计算机中的存储​编辑 b. 文本、图片、音频等数据在计算机中的存储 c. 八进制和十六进制 6. 数据类型 a. …