【C++入门到精通】function包装器 | bind() 函数 C++11 [ C++入门 ]

在这里插入图片描述

阅读导航

  • 引言
  • 一、function包装器
    • 1. 概念
    • 2. 基本使用
    • 3. 逆波兰表达式求值
      • (1)普通写法
      • (2)使用包装器以后的写法
  • 二、bind() 函数
  • 温馨提示

引言

很高兴再次与大家分享关于 C++11 的一些知识。在上一篇文章中,我们讲解了 condition_variable 的使用方法。今天,我们将继续探讨 C++11 中的两个重要概念:function 包装器和 bind() 函数。这两个概念在 C++11 中具有非常重要的作用,它们可以帮助我们更好地管理函数指针和函数对象,并且可以大大提高代码的可读性和可维护性。在接下来的这篇文章中,我们将深入探讨这两个概念的使用方法和注意事项。

一、function包装器

1. 概念

当我们在C++中使用函数指针或函数对象时,经常会遇到一些灵活性和可复用性的问题。C++11引入了一个强大的工具——function包装器,它可以解决这些问题。

function包装器是一个通用的函数封装器可以容纳各种可调用对象(如函数指针、函数对象、成员函数指针等),并提供统一的接口来调用这些可调用对象。通过使用function包装器,我们可以将不同类型的可调用对象存储在一个容器中,方便统一管理和调用。

2. 基本使用

function包装器的使用非常简单。首先,我们需要包含头文件<functional>。然后,我们可以使用std::function模板类来定义一个function对象。例如:

#include <functional>std::function<int(int)> func;

上述代码定义了一个名为func的function对象,它可以接受一个int类型的参数,并返回一个int类型的值。我们可以将各种可调用对象赋值给func,例如函数指针、函数对象、lambda表达式等。例如:

int myFunction(int x) {return x * 2;
}struct MyFunctor {int operator()(int x) {return x + 3;}
};func = myFunction; // 函数指针
int result1 = func(2); // 调用myFunction,结果为4MyFunctor functor;
func = functor; // 函数对象
int result2 = func(2); // 调用functor,结果为5func = [](int x) { return x * x; }; // lambda表达式
int result3 = func(2); // 调用lambda表达式,结果为4

通过使用function包装器,我们可以将不同类型的可调用对象统一存储并调用,极大地提高了代码的灵活性和可复用性。此外,function还提供了其他功能,如判空、交换等。

需要注意的是,function包装器的使用可能会引入一些性能开销,因为它需要进行动态分派。在性能要求较高的场景中,可以考虑使用函数指针或模板来代替function包装器。

3. 逆波兰表达式求值

接下来我们来讲一道题,相信通过这个OJ题目可以让大家更加了解function包装器。首先我先简单介绍一下这个题目:逆波兰表示法(Reverse Polish Notation, RPN)是一种数学表达式的书写方式,它通过将操作符放在两个操作数之后来表示一个算术表达式,从而避免了使用括号。

🔴题目链接

在这里插入图片描述

(1)普通写法

  • 如果当前元素是操作符(‘+’、‘-’、‘*’ 或 ‘/’):

    • 从栈中弹出两个操作数,分别记为 num2 和 num1。
    • 根据当前操作符进行计算,并将结果压入栈中。
  • 如果当前元素是操作数(整数):

    • 将其转换为整数并压入栈中。
  • 遍历结束后,栈中只会剩下一个元素,即为最终的计算结果。

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<long long> st; // 创建一个存储操作数的栈,使用 long long 类型以避免溢出问题for(auto& str : tokens) // 遍历表达式中的每个元素{if(str == "+" || str == "-" || str == "*" || str == "/") // 当前元素为操作符{long long right = st.top(); // 取出栈顶的操作数作为右操作数st.pop(); // 弹出栈顶元素long long left = st.top(); // 取出新的栈顶元素作为左操作数st.pop(); // 弹出栈顶元素// 根据操作符进行计算,并将结果压入栈中switch(str[0]){case '+':st.push(left + right);break;case '-':st.push(left - right);break;case '*':st.push(left * right);break;case '/':st.push(left / right);break;}}else // 当前元素为操作数{st.push(stoll(str)); // 将字符串转换为 long long 类型并压入栈中}}return st.top(); // 返回栈顶元素,即最终的计算结果}
};

(2)使用包装器以后的写法

class Solution {
public:int evalRPN(vector<string>& tokens) {stack<int> st; // 创建一个存储操作数的栈map<string, function<int(int, int)>> opFuncMap = // 创建一个从操作符到对应计算函数的映射表{{ "+", [](int i, int j){return i + j; } }, // lambda 函数实现加法计算{ "-", [](int i, int j){return i - j; } }, // lambda 函数实现减法计算{ "*", [](int i, int j){return i * j; } }, // lambda 函数实现乘法计算{ "/", [](int i, int j){return i / j; } }  // lambda 函数实现除法计算};for(auto& str : tokens) // 遍历表达式中的每个元素{if(opFuncMap.find(str) != opFuncMap.end()) // 当前元素为操作符{int right = st.top(); // 取出栈顶的操作数作为右操作数st.pop(); // 弹出栈顶元素int left = st.top(); // 取出新的栈顶元素作为左操作数st.pop(); // 弹出栈顶元素// 根据操作符进行计算,并将结果压入栈中st.push(opFuncMap[str](left, right));}else // 当前元素为操作数{st.push(stoi(str)); // 将字符串转换为整数并压入栈中}}return st.top(); // 返回栈顶元素,即最终的计算结果}
};

二、bind() 函数

std::bind函数定义在头文件中,是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象(callable object),生成一个新的可调用对象来“适应”原对象的参数列表。一般而言,我们用它可以把一个原本接收N个参数的函数fn,通过绑定一些参数,返回一个接收M个(M可以大于N,但这么做没什么意义)参数的新函数。同时,使用std::bind函数还可以实现参数顺序调整等操作。

  1. 头文件
    bind() 函数位于 <functional> 头文件中,因此在使用该函数之前需要包含该头文件。

  2. 函数原型
    bind() 函数的原型如下所示:

    template <class Fn, class... Args>
    bind(Fn&& fn, Args&&... args);
    
  3. 参数解释

    • Fn:表示函数类型或函数对象类型。
    • Args:表示参数类型。
    • fn:要进行绑定的函数或函数对象。
    • args:要绑定的参数。
  4. 返回值
    bind() 函数返回一个新的可调用对象,该对象可以像原始函数一样被调用,但会自动传递已绑定的参数给 fn

  5. 使用示例
    下面是一个使用 bind() 函数的示例代码:

#include <functional>
#include <iostream>
using namespace std;// 定义一个普通函数 Plus,接收两个 int 类型参数,返回它们的和
int Plus(int a, int b)
{return a + b;
}// 定义一个类 Sub,包含一个成员函数 sub,接收两个 int 类型参数,返回它们的差
class Sub
{
public:int sub(int a, int b){return a - b;}
};int main()
{// 绑定全局函数 Plus 到一个 std::function 对象 func1 上,并使用 placeholders 占位符表示待绑定的参数。// 将第一个和第二个参数分别绑定到调用 func1 时传递的第一个和第二个参数上。std::function<int(int, int)> func1 = std::bind(Plus, placeholders::_1, placeholders::_2);// 使用 auto 定义变量 func2,将全局函数 Plus 绑定到它上面,并将第一个参数和第二个参数分别指定为 1 和 2。// 因为 Plus 函数已经被绑定,这里不需要再使用 placeholders 占位符了。auto func2 = std::bind(Plus, 1, 2);// 创建一个 Sub 对象 s,将它的成员函数 sub 绑定到 std::function 对象 func3 上。// 使用 placeholders 占位符表示待绑定的参数。将第一个和第二个参数分别绑定到调用 func3 时传递的第一个和第二个参数上。Sub s;std::function<int(int, int)> func3 = std::bind(&Sub::sub, s, placeholders::_1, placeholders::_2);// 创建另一个 std::function 对象 func4,将它绑定到 s 的成员函数 sub 上。// 使用 placeholders 占位符表示待绑定的参数。这里将第一个参数和第二个参数分别绑定到调用 func4 时传递的第二个和第一个参数上。std::function<int(int, int)> func4 = std::bind(&Sub::sub, s, placeholders::_2, placeholders::_1);// 分别输出各个函数对象的返回值cout << func1(1, 2) << endl;  // 输出 3,即 Plus(1, 2) 的结果cout << func2() << endl;      // 输出 3,即 Plus(1, 2) 的结果cout << func3(1, 2) << endl;  // 输出 -1,即 s.sub(1, 2) 的结果cout << func4(1, 2) << endl;  // 输出 1,即 s.sub(2, 1) 的结果return 0;
}

这段代码演示了如何使用 std::bind 函数将函数对象和成员函数绑定到 std::function 对象上,并使用占位符 std::placeholders::_1std::placeholders::_2 来表示待绑定的参数。

  1. 我们定义了一个普通的函数 Plus,它接收两个 int 类型的参数并返回它们的和。
  2. 我们定义了一个类 Sub,其中包含一个成员函数 sub,它接收两个 int 类型的参数并返回它们的差。
    • main 函数中,我们首先用 std::bind 将全局函数 Plus 绑定到 std::function 对象 func1 上,使用占位符 std::placeholders::_1std::placeholders::_2 分别表示第一个和第二个参数。这样,我们可以使用 func1 来调用 Plus 函数,并传递实际的参数。
  3. 我们使用 auto 关键字定义了一个变量 func2,将全局函数 Plus 绑定到它上面,并指定第一个参数为 1,第二个参数为 2。因为在这种情况下不需要使用占位符,所以我们直接指定了参数的值。
  4. 我们创建了一个 Sub 类的对象 s,并将其成员函数 sub 绑定到 std::function 对象 func3 上,使用占位符 std::placeholders::_1std::placeholders::_2 分别表示第一个和第二个参数。这样,我们可以通过 func3 调用 s.sub 函数,并传递实际的参数。
  5. 我们创建了另一个 std::function 对象 func4,将其绑定到 s 的成员函数 sub 上,并使用占位符 std::placeholders::_2std::placeholders::_1 分别表示第一个和第二个参数。这样,我们可以通过 func4 调用 s.sub 函数,并以不同的顺序传递实际的参数。

温馨提示

感谢您对博主文章的关注与支持!另外,我计划在未来的更新中持续探讨与本文相关的内容,会为您带来更多关于C++以及编程技术问题的深入解析、应用案例和趣味玩法等。请继续关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。期待与您建立更紧密的互动,共同探索C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述

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

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

相关文章

【Linux系统编程二十六】:线程控制与线程特性(Linux中线程库/线程创建/线程退出/线程等待)

【Linux系统编程二十六】&#xff1a;线程控制与线程特性 一.Linux线程库pthread1.线程控制块2.线程tid3.线程栈 二.线程控制1.线程创建2.线程退出3.线程等待 三.线程的特性1.独立栈2.局部存储3.线程可分离 一.Linux线程库pthread 在Linux中&#xff0c;是没有明确的线程概念的…

19个地信专业可以投的岗位汇总【GIS求职秘籍】

今天给大家详细科普一下&#xff0c;GIS专业的同学毕业以后还能从事哪些岗位的工作。 &#xff08;这期不包含学校老师等事业编岗位&#xff09; 一、GIS数据采集和处理 GIS数据采集和处理在这里分为一个大类&#xff0c;包含前期测绘外业的实地采集&#xff0c;后续的数据加…

石化行业设备管理系统的作用

石化行业是全球经济中不可或缺的重要组成部分&#xff0c;它涵盖了石油、天然气、化工等领域。在这个高风险和高安全要求的行业中&#xff0c;设备的可靠性和安全性至关重要。为了有效管理和维护设备&#xff0c;石化公司越来越多地采用设备管理系统&#xff0c;以提高生产效率…

MongoDB—SQL到MongoDB映射图表

一、术语和概念 下表显示了各种 SQL 术语和概念 以及相应的 MongoDB 术语和概念。 SQL Terms/Concepts MongoDB Terms/Concepts database database table collection row document or BSON document column field index index table joins $lookup, embedded docu…

CSS 伪类函数 :is() 和 :where()

在编写 CSS 时&#xff0c;有时可能会使用很长的选择器列表来定位具有相同样式规则的多个元素。例如&#xff0c;如果您想对标题中的 b 标签进行颜色调整&#xff0c;我们应该都写过这样的代码&#xff1a; h1 > b, h2 > b, h3 > b, h4 > b, h5 > b, h6 > b…

不想root,但想远程控制vivo手机?这个方法不用root也能做到

远程控制vivo手机不用root&#xff01;今天给大家讲讲免Root情况下&#xff0c;笔记本电脑如何远程控制vivo手机。 在电脑和手机都安装AirDroid&#xff0c;这是免Root的关键。 下载AirDroid个人版 | 远程控制安卓手机软件下载下载AirDroid个人版进行文件传输和管理、远程控制安…

八、HTML 链接

一、HTML 链接 HTML 使用超级链接与网络上的另一个文档相连。 HTML中的链接是一种用于在不同网页之间导航的元素。 链接通常用于将一个网页与另一个网页或资源&#xff08;如文档、图像、音频文件等&#xff09;相关联。 链接允许用户在浏览网页时单击文本或图像来跳转到其…

Python进程、线程、协程:多任务并发编程指南

概要 在当今计算机时代&#xff0c;为了提高程序的性能和响应速度&#xff0c;多任务并发编程成为了一种必不可少的技术手段。而Python作为一门高级编程语言&#xff0c;提供了多种多任务并发编程的方式&#xff0c;包括进程、线程和协程。本文将详细介绍这三种方式的使用教程…

Unity 打包AB 场景烘培信息丢失

场景打包成 AB 资源的时候&#xff0c;Unity 不会打包一些自带相关的资源 解决办法&#xff1a;在 Project settings > Graphics下设置&#xff08;Automatic 修改成 Custom&#xff09;

基于web的电影院购票系统

**&#x1f345;点赞收藏关注 → 私信领取本源代码、数据库&#x1f345; 本人在Java毕业设计领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目希望你能有所收获&#xff0c;少走一些弯路。&#x1f345;关注我不迷路&#x1f345;**一 、设计说明 1.1选题动因 …

cnPuTTY 0.80.0.1—PuTTY Release 0.80中文版本简单说明~~

2023-12-18 官方发布了PuTTY 0.80本次发布主要是针对Terrapin攻击(CVE-2023-48795)的修改发布。 更多详细的内容请查看PuTTY Change Log。 有关Terrapin攻击可用简单参考&#xff1a;警告&#xff01;&#xff01;&#xff01;Terrapin攻击(CVE-2023-48795)~~~ 为了缓解此漏洞…

振弦采集仪在地基沉降监测中的应用研究

振弦采集仪在地基沉降监测中的应用研究 振弦采集仪是一种专门用于测量地基沉降的仪器&#xff0c;它采用振弦原理来测量地基的沉降情况。振弦采集仪通过在地基上安装一根细长的弹性振弦&#xff0c;并测量振弦的变形来获得地基沉降的数据。在地基沉降监测中&#xff0c;振弦采…

TypeScript Array(数组)

目录 1、数组初始化 2、Array 对象 3、数组迭代 4、数组在函数中的使用 4.1、作为参数传递给函数 4.2、作为函数的返回值 5、数组方法 数组对象是使用单独的变量名来存储一系列的值。数组非常常用。假如你有一组数据&#xff08;例如&#xff1a;网站名字&#xff09;…

从“五力”看百亿西凤的必然性

执笔 | 文 清 编辑 | 萧 萧 2023年末&#xff0c;西凤成功突破市场阻碍、跑赢行业周期&#xff0c;正式跻身中国百亿白酒品牌阵容。这是一份全行业及全体西凤人“预期之内”的成绩单。 当下&#xff0c;中国白酒已经进入“存量竞争”时代&#xff0c;马太效应使得强者恒强…

8-链表-旋转链表

这是链表的第8篇算法&#xff0c;力扣链接。 给你一个链表的头节点 head &#xff0c;旋转链表&#xff0c;将链表每个节点向右移动 k 个位置。 示例 1&#xff1a; 输入&#xff1a;head [1,2,3,4,5], k 2 输出&#xff1a;[4,5,1,2,3]示例 2&#xff1a; 输入&#xff1a;h…

[足式机器人]Part2 Dr. CAN学习笔记-Ch01自动控制原理

本文仅供学习使用 本文参考&#xff1a; B站&#xff1a;DR_CAN Dr. CAN学习笔记-Ch01自动控制原理 1. 开环系统与闭环系统Open/Closed Loop System1.1 EG1: 烧水与控温水壶1.2 EG2: 蓄水与最终水位1.3 闭环控制系统 2. 稳定性分析Stability2.1 序言2.2 稳定的分类2.3 稳定的对…

【PTA-C语言】编程练习5 - 函数与指针

如果代码存在问题&#xff0c;麻烦大家指正 ~ ~有帮助麻烦点个赞 ~ ~ 编程练习5 - 函数与指针 6-1 求实数和的函数&#xff08;分数 10&#xff09;6-2 求解一元二次方程实根的函数&#xff08;分数 10&#xff09;6-3 求集合数据的均方差&#xff08;分数 10&#xff09;6-4 计…

天融信TOPSEC Cookie 远程命令执行漏洞

产品介绍 天融信TopSec 安全管理系统&#xff0c;是基于大数据架构&#xff0c;采用多种技术手段收集各类探针设备安全数据&#xff0c;围绕资产、漏洞、攻击、威胁等安全要素进行全面分析&#xff0c;提供统一监测告警、集中策略管控、协同处置流程&#xff0c;实现客户等保合…

FindMy技术用于鼠标

鼠标是计算机的标准配置之一&#xff0c;其设计初衷是为了使计算机的操作更加简便快捷&#xff0c;减少用户在操作中的负担。用户可以通过移动鼠标&#xff0c;实现光标的精确移动&#xff0c;进而选择、拖拽、复制、粘贴等操作。这种操作方式&#xff0c;使得计算机的操作变得…

人工智能未来发展前景怎么样?

人工智能的未来发展前景怎么样&#xff1f;人工智能的未来发展前景非常广阔&#xff0c;有以下几个方面的发展趋势和前景&#xff1a; 1、人工智能的未来发展前景-应用领域扩展&#xff1a;人工智能将在各个领域得到广泛应用&#xff0c;包括医疗保健、金融、交通、制造业、农业…