【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.rhkb.cn/news/230215.html

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

相关文章

JDK、JRE、JVM的联系与区别

JDK、JRE、JVM的联系与区别 一、JDK,JRE,JVM定义 JDK(Java Development Kit),包含JRE,以及增加编译器和调试器等用于程序开发的文件。 JRE(Java Runtime Environment)&#xff0c;包含Java虚拟机、库函数、运行Java应用程序所必须的文件。 JVM(Java Virtual Machine)是一个虚…

c++ 类和对象

目录 基本概念类的定义类的基本使用对象的实例化访问控制符 面向对象程序设计方法实例 构造函数和析构函数构造函数定义总结 析构函数定义作用 多个对象构造和析构 对象的动态建立和释放new和deletenew delete和malloc free区别 对象的赋值利用实例化好的对象对另外一个对象初始…

力扣hot100 二叉树的直径

&#x1f468;‍&#x1f3eb; 题目地址 一个节点的最大直径 它左树的深度 它右树的深度 &#x1f60b; AC code /*** Definition for a binary tree node.* public class TreeNode {* int val;* TreeNode left;* TreeNode right;* TreeNode() {}* Tr…

5.云原生安全之ingress配置域名TLS证书

文章目录 cloudflare配置使用cloudflare托管域名获取cloudflare API Token在cloudflare中配置SSL/TLS kubesphere使用cert-manager申请cloudflare证书安装证书管理器创建Secret资源创建cluster-issuer.yaml创建cert.yaml申请证书已经查看申请状态 部署harbor并配置ingress使用证…

C++上位软件通过Snap7开源库访问西门子S7-200/合信M226ES数据块的方法

前言 上一篇文章中介绍了Snap7访问西门子S7-1200/S7-1500 DB块的方法&#xff0c;对于S7-200PLC是没有数据块访问的。S7-200PLC中Snap7只能通过访问MB块&#xff0c;VB块的方法进行和PLC之间的Snap7通信和数据交换。手头没有S7-200PLC故通过合信CTMC M226ES运动控制器进行测试&…

魔改版小市值策略

策略思路 最近几年&#xff0c;小市值策略一直都收益不错&#xff08;当然&#xff0c;不包含17年和18年&#xff09;。小市值因子对收益的影响是很大的。特别是行情不好的时候&#xff0c;大家都忙着炒作热点&#xff0c;那么这时候符合题材的小市值更加符合炒作标准了。 为…

Superset服务安装

文章目录 Superset概述Superset应用场景Superset安装及使用安装Python环境安装Miniconda下载Miniconda(Python3版本)安装Miniconda取消每次登陆自动激活conda base环境创建Python3.7(Superset)环境配置conda国内镜像创建Superset环境激活Superset环境查看python版本 Superset部…

AWS(三):如何在AwsManagedAd目录和windowsAD实例之间建立双向信任。

前提&#xff1a; 1.创建好了一个AWS managed AD目录&#xff0c;我的目录域名为:aws.managed.com 2.创建好了一个windows AD实例并提升了为域控服务器,实例域名为:aws2.com 看过我AWS 一和二的应该都会创建windows实例了&#xff0c;切记不能将其无缝加入到aws managed AD的…

Vue 模板编译原理解析

Vue 模板编译原理解析 模板编译整体流程 首先我们看一下什么是编译&#xff1f; 所谓编译&#xff08;Compile&#xff09;&#xff0c;指的是将语言 A 翻译成语言 B&#xff0c;语言 A 就被称之为源码&#xff08;source code&#xff09;&#xff0c;语言 B 就被称之为目标…

【QML】与 C++ 混合编程:互相调用函数

文章目录 qml 调用 C 函数案例 a&#xff1a;Q_INVOKABLE 标记 C 函数 视图设置进 qml 属性案例 b&#xff1a;qml 通过发送信号的方式&#xff0c;调用 Qt 槽函数 C调用qml函数 qml 调用 C 函数 qml 要使用 C 的函数有两个方法&#xff1a; 一种是&#xff0c;用 Q_INVOKABLE…

STM32 学习(三)OLED 调试工具

目录 一、简介 二、使用方法 2.1 接线图 2.2 配置引脚 2.3 编写代码 三、Keil 工具调试 一、简介 在进行单片机开发时&#xff0c;有很多调试方法&#xff0c;如下图&#xff1a; 其中 OLED 就是一种比较好用的调试工具&#xff1a; OLED 硬件电路如下&#xff0c…

7步教你如何快速建立电子商务网站

如果您需要以客户为中心搭建电子商务网站&#xff0c;或者您正在寻求开展电子商务业务&#xff0c;那么您很幸运——现在是开始的最佳时机&#xff01;事实上&#xff0c;在疫情期间&#xff0c;电子商务销售额增长了 50%。电子商务现在是一个价值 8700 亿美元的产业。随着如此…

深入了解Apache 日志,Apache 日志分析工具

Apache Web 服务器在企业中广泛用于托管其网站和 Web 应用程序&#xff0c;Apache 服务器生成的原始日志提供有关 Apache 服务器托管的网站如何处理用户请求以及访问您的网站时经常遇到的错误的重要信息。 什么是 Apache 日志 Apache 日志包含 Apache Web 服务器处理的所有事…

rime中州韵小狼毫 inputShow lua Filter 输入字符透传滤镜

在 rime中州韵小狼毫 inputShow lua Translator 一文中&#xff0c;我们通过 inputShow.lua 定制了 inputShow_translator&#xff0c;这使得我们的输入方案可以将用户输入的字符透传到候选列表中来。如下&#x1f447;&#xff1a; &#x1f446;上图中我们在候选列表中看到了…

音视频通信

文章目录 一、音视频通信流程二、流媒体协议1、RTSP2、RTMP3、HLS4、WebRTC 一、音视频通信流程 音视频通信完整流程有如下几个环节&#xff1a;采集、编码、前后处理、传输、解码、缓冲、渲染等。 每一个细分环节&#xff0c;还有更细分的技术模块。比如&#xff0c;前后处…

[足式机器人]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 稳定的对…

[蓝桥杯2020国赛]答疑

答疑 题目描述 有 n 位同学同时找老师答疑。每位同学都预先估计了自己答疑的时间。 老师可以安排答疑的顺序&#xff0c;同学们要依次进入老师办公室答疑。 一位同学答疑的过程如下&#xff1a; 首先进入办公室&#xff0c;编号为 i 的同学需要 si​ 毫秒的时间。然后同学问…

2020年认证杯SPSSPRO杯数学建模D题(第一阶段)让电脑桌面飞起来全过程文档及程序

2020年认证杯SPSSPRO杯数学建模 D题 让电脑桌面飞起来 原题再现&#xff1a; 对于一些必须每天使用电脑工作的白领来说&#xff0c;电脑桌面有着非常特殊的意义&#xff0c;通常一些频繁使用或者比较重要的图标会一直保留在桌面上&#xff0c;但是随着时间的推移&#xff0c;…

Java数据结构:1. 数据结构前置知识

文章目录 一、初识数据结构二、初识集合框架1. 什么是集合框架2. 集合框架的重要性3. 背后所涉及的数据结构以及算法 三、时间复杂度空间复杂度1. 算法效率2. 时间复杂度&#xff08;1&#xff09;概念&#xff08;2&#xff09;大O的渐进表示法&#xff08;3&#xff09;推导大…

捕捉“五彩斑斓的黑”:锗基短波红外相机的多种成像应用

红外处于人眼可观察范围以外&#xff0c;为我们了解未知领域提供了新的途径。红外又可以根据波段范围&#xff0c;分为短波红外、中波红外与长波红外。较短的SWIR波长——大约900nm-1700nm——与可见光范围内的光子表现相似。虽然在SWIR中目标的光谱含量不同&#xff0c;但所产…