【C++】:bind绑定器和function函数对象机制

欢迎来到 破晓的历程的 博客

⛺️不负时光,不负己✈️

文章目录

    • 引言
    • function函数对象
      • function引入
      • 细讲function
      • 体验function在工程实践中的优势
    • 模拟实现function函数对象机制
    • bind绑定器
      • 基本语法
      • 示例
        • 1. 绑定普通函数
        • 2. 使用占位符
        • 3. 绑定成员函数
        • 4. 绑定 lambda 表达式
      • 注意事项
    • 使用bind+function完成线程池设计
      • 何为线程池?
      • 设计代码

引言

在C++中工程实践中,bind绑定器和function函数对象非常常用,而且bind+function简直就是无敌的存在。本篇博客中,我们力求用最小的成本搞懂它们,让你用起来得心应手

function函数对象

以减少理解成本为目地,我们先学习function。

function引入

function的作用是将具有相同调用形式的不同类型可调用对象进行类型统一。

相同的调用形式可以简单理解为:参数列表和返回值相同。

C++常见可调用对象有:函数、指针、匿名函数(lambda表达式)、函数对象(重载了函数调用运算符的类)以及使用bind创建的对象。
例如以下几个可调用对象具有相同的调用形式:

// 函数
int add(int a, int b) {return a + b;
}// lambda表达式
auto sub = [](int a, int b) -> int {return a - b;
};// 函数对象
class prod{
public:int operator() (int a, int b) {return a * b;}
};

这些可调用对象的调用形式相同,甚至拥有相同的功能,但是却由于类型千差万别无法统一处理。例如,我们想要统一调用以上可调用对象,使用‘+’来调用add函数,而使用‘-’调用sub匿名函数…
使用分支语句(if else if else)当然是可以做到的,但更推荐以下方式:

/*  使用map映射字符到可调用对象。但需要注意,映射的前提是function对不同类型的可调用对象
进行了类型统一。 */
map<char, function<int(int, int)>> cacul{{'+', add},{'-', sub},{'*', prod()}
};cout << cacul['*'](5, 4) << endl;

细讲function

funciotn是从c++11开始支持的特性,使用它需要包含头文件functional
在cppreference中解释为:类模板std::function是一个通用的多态函数包装器。std::function的实例可以对任何可以调用的目标实体进行存储、复制、和调用操作,这些目标实体包括普通函数、Lambda表达式、函数指针、以及其它函数对象等。
通俗的来说可以把它当做一个函数指针来使用

让我们来感受一下:
function的模板是 std::function<返回值类型(传入参数类型)> 方法名
这里传入参数类型可以是自己定义的

举几个简单的例子:

function<int(int, int)> func

这表示:定义了一个返回值为int,参数有两个,从左往右为int,int类型的函数指针

int sum(int a, int b)
{return a + b;
}
int main()
{using func_t = function<int(int, int)>;func_t func = sum;cout<<func(10,10);
}

这里,我创建了一个返回值为int,参数有两个,从左往右为int,int类型的函数指针。然后用该函数指针创建一个对象func,将sum「函数名表示该函数的地址」赋值给func。然后就可以通过func调用sum函数。
或者也可以这样:

int main()
{function<int(int, int)> func = sum;cout<<func(10,10);
}

用也许这样更容易被理解。

体验function在工程实践中的优势

假如我们要设计一个图书管理系统,该系统提供的服务有:借书、查询书、还书。假设这些函数的函数签名都是一样的「即返回值类型和参数类型都是相同的」。我们习惯将服务编号,比如用户输入1表示要获取借书服务,用户输入2表示查询书服务,用户输入3表示还书服务。
此时的开发者有两种设计方案「大体框架,不包括不同模块设计的具体细节」

  • 使用switch判断语句,但是这种框架的劣性在于如果增加一个模块需要进行大范围修改。
  • 使用function和map数据结构相结合。

接下来我们重点介绍第二种框架
本着说明问题,其他一切从简的原则 我们可以这样设计


#include<iostream>
#include<functional>
#include<map>
using namespace std;
void borrow()
{std::cout << "borrow books" << std::endl;
}
void lend()
{std::cout << "Return the book" << std::endl;}
void search()
{std::cout << "Search book" << std::endl;
}
int main()
{using func_t = std::function<void()>;std::map<int, func_t> Map;Map.insert({ 1,borrow });Map.insert({ 2,lend });Map.insert({ 3,search });while (1){int option = 0;std::cout << "请选择:";std::cin >> option;auto it = Map.find(option);if (it == Map.end()){cout << "没有这项服务,请重新选择:" << endl;}else{it->second();}}
}

模拟实现function函数对象机制

如上,我们系统的介绍了function的使用方法和应用,但是它究竟是如何实现的呢?我们一切来探究一下

#include<iostream>
#include<string>
template<class R,class A>
class myfunction<R(A)>
{
public:using PFUNC = R(*)A;myfunction(PFUNC pfunc):_pfunc(pfunc){}R operator()(A ter){return _pfunc(str);}
priavte:PFUNC _pfunc;};
void hello(std::string str)
{std::cout << str << std::endl;
}
int main()
{myfunction<void(std::string)> func_t = hello;func_t("hello world");
}

但是如果函数签名发生变化,我们就得写不同的部分特例化模板,其实我们可以这样做:

template<class T,class... A1>
class my_function<T(A1...)>
{
public:using func_t = T(*)(A1...);my_function(func_t func):func_(func){}R operator()(A1... args){return func_(args...);}
private:func_t func_;
};

这就是模板的强大之处。

bind绑定器

关于bind绑定器,百度百科是这样说的:
std::bind 是 C++11 引入的一个标准库函数,它位于 functional 头文件中。std::bind 可以用来绑定函数的参数,或者将成员函数和对象绑定在一起,生成一个新的可调用对象(也称为函数对象)。这个新生成的对象可以像普通函数一样被调用,但其内部实际上会调用我们最初绑定的那个函数或成员函数。

基本语法

#include <functional> // 包含 std::bind 的头文件auto bound_function = std::bind(function, arg1, arg2, ..., argN);
  • function 是要绑定的函数或可调用对象。
  • arg1, arg2, ..., argN 是传递给 function 的参数,可以是具体的值,也可以是占位符 _1, _2, ...(这些占位符定义在 <placeholders> 头文件中,通常通过 std::placeholders::_1 等方式访问)。

示例

1. 绑定普通函数
#include <iostream>
#include <functional>void print_sum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;
}int main() {auto bound_print_sum = std::bind(print_sum, 5, 10);bound_print_sum(); // 输出: Sum: 15return 0;
}
2. 使用占位符
#include <iostream>
#include <functional>
#include <placeholders> // 包含 std::placeholders 的头文件void print_sum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;
}int main() {using namespace std::placeholders;auto bound_print_sum = std::bind(print_sum, _1, 10);bound_print_sum(5); // 输出: Sum: 15bound_print_sum(20); // 输出: Sum: 30return 0;
}
3. 绑定成员函数
#include <iostream>
#include <functional>
#include <placeholders>class MyClass {
public:void print_sum(int a, int b) {std::cout << "Sum: " << a + b << std::endl;}
};int main() {MyClass obj;using namespace std::placeholders;auto bound_print_sum = std::bind(&MyClass::print_sum, &obj, _1, 10);bound_print_sum(5); // 输出: Sum: 15return 0;
}

注意,在绑定成员函数时,第一个参数需要是对象的指针或引用。

4. 绑定 lambda 表达式
#include <iostream>
#include <functional>
#include <placeholders>int main() {auto lambda = [](int a, int b) { std::cout << "Sum: " << a + b << std::endl; };auto bound_lambda = std::bind(lambda, _1, 20);bound_lambda(10); // 输出: Sum: 30return 0;
}

注意事项

  1. 性能std::bind 生成的函数对象在调用时,可能会引入额外的间接调用开销,因此在某些性能敏感的场景中,需要谨慎使用。
  2. 兼容性std::bind 是 C++11 引入的特性,因此确保你的编译器支持 C++11 或更高版本。
  3. 替代方案:在 C++11 及以后的版本中,lambda 表达式通常是一个更灵活和直观的选择,用于实现类似的功能。

通过上面的示例和解释,你应该对 std::bind 的基本用法有了初步的了解。在实际开发中,根据具体需求选择合适的工具和方法来实现功能。

使用bind+function完成线程池设计

何为线程池?

线程池是一种高效的设计方案。我们事先准备若干个线程,然后给不同的线程分配不同的任务。

设计代码

#include<iostream>
#include<functional>
#include<string>
#include<thread>
#include<vector>
using namespace std;
using namespace std::placeholders;
class Thread
{
public:Thread(function<void()>Way):_Way(Way){}thread start(){thread t(_Way);return t;}
private:function<void()> _Way;
};
class ThreadPoll
{
public:ThreadPoll(){}~ThreadPoll(){for (auto it : _poll){delete it;}}void start(int size){for (int i = 0; i < size; i++){_poll.push_back(new Thread(std::bind(&ThreadPoll::ThreadWay, this, i)));}for (auto it : _poll){_hander.push_back(it->start());}for (auto &it : _hander){it.join();}}
private:void ThreadWay(int i){cout << "thread %d run........" << i << endl;}vector<Thread*> _poll;vector<thread> _hander;
};
int main()
{ThreadPoll it;it.start(20);
}
``

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

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

相关文章

【汇编语言】寄存器(CPU工作原理)(六)—— 修改CS,IP的指令以及代码段

文章目录 前言1. 修改CS、IP的指令2. 问题分析:CPU运行的流程3. 代码段小结结语 前言 &#x1f4cc; 汇编语言是很多相关课程&#xff08;如数据结构、操作系统、微机原理&#xff09;的重要基础。但仅仅从课程的角度出发就太片面了&#xff0c;其实学习汇编语言可以深入理解计…

基于SpringBoot在线拍卖系统【附源码】

基于SpringBoot在线拍卖系统 效果如下&#xff1a; 网站首页界面 用户登录界面 竞拍商品界面 管理员登录界面 管理员功能界图 竞拍商品界面 系统界面 订单界面 研究背景 随着社会的发展&#xff0c;信息化时代带来了各行各业的变革。电子商务已成为人们日常生活不可或缺的一…

【重学 MySQL】四十四、相关子查询

【重学 MySQL】四十四、相关子查询 相关子查询执行流程示例使用相关子查询进行过滤使用相关子查询进行存在性检查使用相关子查询进行计算 在 select&#xff0c;from&#xff0c;where&#xff0c;having&#xff0c;order by 中使用相关子查询举例SELECT 子句中使用相关子查询…

刷题 -哈希

面试面试经典 150 题 - 哈希 383. 赎金信 - 一个哈希表搞定 class Solution { public:bool canConstruct(string ransomNote, string magazine) {int hash[26] {0};for (auto& ch : magazine) {hash[ch - a];}for (auto& ch : ransomNote) {if (--hash[ch - a] < …

Linux的六个入侵检查思路及预防

背景 入侵检查是保障计算机安全运行的重要手段之一&#xff0c; 通过操作系统的静态配置分析、日志分析、异常行为分析以及文件完整性等方式来做检查&#xff0c;来判断我们的操作系统是否有受到入侵。今天阿祥就介绍十个简单的入侵检查思路及应对措施&#xff0c;希望对大家有…

原生USDC正式上线Sui

今天&#xff0c;标志着Sui生态的一个重要里程碑 — — 原生USDC现已正式在Sui主网上线。作为最广泛使用的稳定币之一&#xff0c;USDC为日益增长的Sui生态带来了稳定的价值传输和流动性。 随着Sui DeFi锁仓量&#xff08;TVL&#xff09;突破10亿美元&#xff0c;网络上需要更…

Linux同时安装多个JDK

Linux同时安装多个JDK 一、JDK1.1、JDK的下载1.2、解压并放置目录 二、通过alias切换版本2.1、修改profile文件2.2、使用和验证 三、使用update-alternatives工具3.1、修改profile文件3.2、指定JDK版本3.3、使用和验证 四、总结 一、JDK 1.1、JDK的下载 JDK官网下载&#xff…

无人机之飞行算法篇

无人机的飞行算法是一个复杂而精细的系统&#xff0c;它涵盖了多个关键技术和算法&#xff0c;以确保无人机能够稳定、准确地执行飞行任务。 一、位置估计 无人机在空中飞行过程中需要实时获取其位置信息&#xff0c;以便进行路径规划和控制。这通常通过以下传感器实现&#…

Rust编程中的循环语句

【图书介绍】《Rust编程与项目实战》-CSDN博客 《Rust编程与项目实战》(朱文伟&#xff0c;李建英)【摘要 书评 试读】- 京东图书 (jd.com) Rust编程与项目实战_夏天又到了的博客-CSDN博客 6.2 for 循 环 迭代次数是确定/固定的循环称为确定循环。for 循环是一个确定循环…

新书速览|你好,C++

《你好&#xff0c;C》 本书内容 《你好&#xff0c;C》主要介绍C开发环境的搭建、基础语法知识、面向对象编程思想以及标准模板库的应用&#xff0c;特别针对初学者在学习C过程中可能遇到的难点提供了解决方案。全书共分13章&#xff0c;以一个工资程序的不断优化和完善为线索…

速度白嫖:Minimax海螺上线图生视频功能

一、什么是Minimax海螺 网址&#xff1a;https://hailuoai.video/ Minimax海螺是一款创新的内容创作工具&#xff0c;专注于将静态图像转化为动态视频。它利用先进的图像处理与生成算法&#xff0c;帮助用户将普通图片迅速转变为引人入胜的短视频&#xff0c;适合社交媒体、…

【HarmonyOS开发笔记 1】 -- 开发环境的搭建

DevEco Studio 的下载与安装 下载 下载路径&#xff1a; https://developer.huawei.com/consumer/cn/download/ 安装 解压后双击 deveco-studio-5.0.3.814.exe 指定安装目录&#xff0c;或者默认&#xff0c;然后下一步 一直“下一步”&#xff0c; 直到最后安装完成 新…

视频消重pr模板|胶片损伤特效视频去重pr模板工程文件

可以用于视频消重效果的pr去重模板&#xff0c;10种胶片损伤特效视频叠加素材pr工程文件。 Premiere Pro模板&#xff0c;可以使用这些效果来增强您的媒体。音乐不包括在内。 下载地址&#xff1a;Pr模板网 下载链接&#xff1a;https://prmuban.com/40591.html

分享我“Excel 表格”关键字的博客笔记(python脚本全程自动)

Python脚本全程自动&#xff0c;全部Python内建工具脚本纯净。 (笔记模板由python脚本于2024年10月05日 19:51:06创建&#xff0c;本篇笔记适合喜欢Excel和Python的coder翻阅) 【学习的细节是欢悦的历程】 Python 官网&#xff1a;https://www.python.org/ Free&#xff1a;大…

Qt - QMenu

QMenu 1、menu转string输出 //GlobalEnum.h #include <QObject> #include <QMetaEnum> class GlobalEnum : public QObject {Q_OBJECT public:EnumTest();enum Enum_Test{ZhangSan 0,WangWu,};Q_ENUM(Enum_Test) };#define EnumToString(e) \ QMetaEnum::fromTy…

手把手教你如何配置好VS Code的WEB基础开发环境(保姆级)

1. VS Code介绍 微软旗下的多场景开发环境软件&#xff0c;支持JAVA、C、C#、C、WEB、VUE、CSS、HTML、Python等等等 如果你刚刚开始编程或者准备学习WEB&#xff0c;那么我强烈建议你使用这款软件 缺点&#xff1a;&#xff08;针对初学者&#xff09; 需要安装各种各样的插…

数据分析-29-基于pandas的窗口操作和对JSON格式数据的处理

文章目录 1 窗口操作1.1 滑动窗口思想1.2 函数df.rolling2 JSON格式数据2.1 处理简单JSON对象和JSON列表2.1.1 处理简单的JSON结构2.1.2 处理空字段2.1.3 获取部分字段2.2 处理多级json2.2.1 展开所有级别(默认)2.2.2 自定义展开层级2.3 处理嵌套列表JSON3 参考附录1 窗口操作 …

每日学习一个数据结构-图

文章目录 图基础一、图的定义二、图的相关概念三、图的分类四、图的使用场景 和图相关的算法一、图的遍历算法二、最短路径算法三、最小生成树算法四、图匹配算法五、网络流算法 图基础 一、图的定义 在数学中&#xff0c;图是描述于一组对象的结构&#xff0c;其中某些对象对…

YOLOv11模型地址

地址链接 项目Git地址&#xff1a;https://github.com/ultralytics/ultralytics?tabreadme-ov-file

大模型生成PPT大纲优化方案:基于 nVidia NIM 平台的递归结构化生成

大模型生成PPT大纲优化方案&#xff1a;基于 nVidia NIM 平台的递归结构化生成 待解决的问题 生成PPT大纲是一种大模型在办公场景下应用的常见需求。 然而&#xff1a; 目前直接让大模型生成大纲往往是非结构化的&#xff0c;输出格式多样&#xff0c;难以统一和规范&#…