探索仿函数(Functor):C++中的灵活函数对象

文章目录

  • 一、仿函数定义及使用
  • 二、仿函数与函数指针的区别
  • 三、仿函数与算法的关系
  • 四、仿函数的实践用例

在C++编程中,我们经常需要对数据进行排序、筛选或者其他操作。为了实现这些功能,C++标准库提供了许多通用的算法和容器,而其中一个重要的概念就是 仿函数(Functor)。仿函数是一种行为类似函数的对象,它可以被当做函数使用,作为算法的参数传递,或者在容器中使用。本文将深入探讨仿函数的概念、用法以及在实际开发中的应用,希望能够帮助读者更好地理解和应用仿函数这一重要的编程概念。

一、仿函数定义及使用

仿函数(Functor) 是一种行为类似函数的对象,它可以被用作函数并接受参数。在C++中,仿函数通常是重载了函数调用运算符operator()的类对象。通过重载operator(),仿函数可以像函数一样被调用,并且可以保存状态信息。

按照操作数划分:假定某个类有一个重载的operator(),而且重载的operator()要求获取一个参数,我们就将这个类称为一元仿函数(unary functor);如果重载的operator()要求获取两个参数,就将这个类称为二元仿函数(binary functor)

按照功能划分:可分为算数运算(Arithmetic)关系运算(Rational)、**逻辑运算(Logical)**三大类。

一个简单的例子是比较仿函数,它可以用来对数据进行排序。例如,可以定义一个比较仿函数来比较两个整数的大小,并将其传递给STL中的sort函数来进行排序。

以下是一个简单的比较仿函数的示例:

// 定义一个比较仿函数
template<class T>
struct LessThan {bool operator()(T a, T b) const { return a < b;}
};
int main() {LessThan<int> lessThan; // 创建比较仿函数对象std::cout << lessThan(3, 5) << std::endl; // 使用仿函数对象进行比较std::cout << LessThan<int>()(3, 5) << std::endl; // 使用仿函数对象进行比较return 0;
}

在这个例子中,LessThan是一个仿函数,重载了operator()来进行比较。在main函数中,我们创建了LessThan的对象lessThan,并使用它来比较两个整数的大小。要将某种操作当做算法的参数,唯一的办法就是先将该操作(可能用于数条以上的指令)设计为一个函数,再将函数指针当作一个算法的一个参数,或是将该操作设计为一个所谓的仿函数(从语言层次来看就是一个class),再以该仿函数产生一个对象,并以此对象作为算法参数的一部分。

//一元仿函数,
struct SetKeyOfT{const K& operator()(const K& key){ return key; }
};
struct MapKeyOfT{const K& operator()(const pair<K, V>& kv) { return kv.first; }
};

这两个仿函数(也称为函数对象)分别用于提取不同类型数据的键(key)。仿函数的主要目的是提供一个可调用对象,通常用于算法中作为自定义的比较或操作函数。

仿函数SetKeyOfT是设计用来从单独的键(类型为K)中提取键本身。它重载了operator(),使其能够接受一个类型为K的常量引用作为参数,并返回该键的常量引用。

仿函数MapKeyOfT用于从pair<K, V>中提取键(first成员)。在C++标准库中,pair是一个模板类,通常用于表示键值对,例如在mapunordered_map等容器中。这个仿函数通过重载operator(),使其能够接受一个pair<K, V>类型的常量引用作为参数,并返回该pair的键(first成员)的常量引用。

这在实际应用中可能看起来有些多余,但是在我们模仿实现一些数据结构时可以起到作用(如仿写set 和 map)。 通过使用仿函数,我们可以将函数对象作为参数传递给其他函数,或者将其存储在容器中,从而实现更灵活的编程和功能组合。


二、仿函数与函数指针的区别

函数指针也可以当作参数传递给算法当参数。但是函数指针不能满足 STL对抽象性的要求,即无法和STL其他组件搭配。

在STL(标准模板库)中,仿函数(也称为函数对象)与算法之间存在着密切的关系。STL算法通常接受仿函数作为参数,以便能够自定义算法的行为。这种灵活性使得STL算法能够适用于各种不同的场景,而不仅仅是预定义的操作。

仿函数(functor)和函数指针在编程中虽然都有其独特的应用场景,但它们在实现方式、使用灵活性和抽象层次等方面存在显著的区别。

首先,仿函数是一个类的实例,通过重载operator()操作符,使得这个类的对象可以像函数一样被调用。这使得仿函数在行为上表现得像一个函数,但实际上它拥有类的所有特性,如数据成员和成员函数。因此,仿函数可以封装更复杂的状态和行为,并且可以在类定义中提供更多的灵活性和控制。

相比之下,函数指针是一个指向函数的指针变量。它本身是一个指针,指向的是函数的入口地址。函数指针主要用于在运行时动态地调用不同的函数,或者将函数作为参数传递给其他函数。然而,函数指针的使用受到一定的限制,因为它只能指向已定义的函数,而不能封装更复杂的状态或行为。

在抽象层次上,仿函数提供了比函数指针更高层次的抽象。仿函数可以看作是函数指针的泛化,它不仅能够像函数指针一样动态地调用不同的函数,还能够封装更多的状态和行为。这使得仿函数在使用STL算法等需要高度抽象和灵活性的场合中更为适用。


三、仿函数与算法的关系

首先仿函数在STL中的作用是极大的。

仿函数在STL中的主要作用是提供一种可以像函数一样调用的对象。它们通常通过重载operator()来定义自己的行为,从而可以在算法中作为参数传递,以决定算法如何操作元素。

算法通常与仿函数进行结合

STL算法是高度通用化的,它们通过接受仿函数作为参数来适应不同的操作需求。例如,std::sort算法可以对容器进行排序,但它并不直接定义如何比较元素。相反,它接受一个仿函数作为参数,该仿函数定义了如何比较元素。这样,你可以为不同的数据类型或排序需求提供不同的比较逻辑。

在这里插入图片描述

同样,std::transform算法可以对容器中的元素进行转换,它接受一个仿函数来定义转换的逻辑。你可以提供一个仿函数来执行任何你想要的转换操作。下面是一个使用仿函数进行排序的示例:

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 定义一个仿函数,用于比较两个整数的大小
struct CompareInts {bool operator()(const int& a, const int& b) const{ return a < b; }// 升序比较
};
int main() {vector<int> vec = {5, 2, 8, 1, 9};// 使用sort算法和自定义的仿函数进行排序sort(vec.begin(), vec.end(), CompareInts());return 0;
}

在这个例子中,我们定义了一个名为CompareInts的仿函数,它重载了operator()来定义如何比较两个整数。然后,我们将这个仿函数作为第三个参数传递给sort算法,以便按照升序对整数进行排序。

仿函数比起一般函数具有很多优点,以下描述错误的是( C )
A.在同一时间里,由某个仿函数所代表的单一函数,可能有不同的状态
B.仿函数即使定义相同,也可能有不同的类型
C.仿函数通常比一般函数速度快
D.仿函数使程序代码变简单

A.仿函数是模板函数,可以根据不同的类型代表不同的状态
B.仿函数是模板函数,可以有不同类型
C.仿函数是模板函数,其速度比一般函数要慢,故错误
D.仿函数在一定程度上使代码更通用,本质上简化了代码

⚠️我们如果要使用STL内建的仿函数,都必须含 <functional>头文件。


四、仿函数的实践用例

我们拿leetoce中的题目来做案例:692. 前K个高频单词 - 力扣(LeetCode)。题目如下:

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

示例 1:

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。注意,按字母顺序 "i" 在 "love" 之前。

示例 2:

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,出现次数依次为 4, 3, 2 和 1 次。

注意:

  • 1 <= words.length <= 500
  • 1 <= words[i] <= 10
  • words[i] 由小写英文字母组成。
  • k 的取值范围是 [1, 不同 words[i] 的数量]
class Solution {
public:class Com{public:bool operator()(const pair<string,int> &kv1, const pair<string,int> &kv2){return kv1.second > kv2.second || (kv1.second == kv2.second && kv1.first < kv2.first) ;}};vector<string> topKFrequent(vector<string>& words, int k) {map<string,int > mp;for(auto &e : words)mp[e]++;vector<pair<string,int>> ans(mp.begin(),mp.end()); sort(ans.begin(),ans.end(),Com());auto it = ans.begin();vector<string> ret;while(k--){ret.push_back(it->first);it++;}return ret;}
};

Com是一个嵌套的仿函数,它重载了operator()以提供自定义的比较逻辑。这个仿函数用于对pair<string, int>类型的元素进行比较,其中string代表单词,int代表该单词的出现频率。

整体解题思路如下:

  1. 统计频率topKFrequent函数首先遍历输入的字符串数组words,并使用map数据结构mp统计每个单词的出现频率。
  2. 创建向量:接着,它将map中的元素(键值对)复制到一个vector<pair<string, int>>类型的向量ans中。
  3. 排序:然后,它使用sort函数对ans进行排序。排序时使用了之前定义的仿函数Com作为比较函数,因此排序结果会按照单词频率的降序和字典序的升序进行排列。
  4. 提取结果:最后,程序使用一个迭代器it遍历排序后的ans,并将前k个单词(即频率最高的k个单词)添加到结果向量ret中。
  5. 返回结果:函数返回包含前k个最频繁单词的ret向量。

本题通过使用仿函数实现了自定义的比较逻辑,这种比较逻辑确保了在排序后,出现频率高的单词会排在前面,如果频率相同,则字典序小的单词排在前面。使得我们可以按照特定的顺序对单词进行排序,并最终提取出出现频率最高的前k个单词。

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

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

相关文章

nut-ui组件库icon中使用阿里图标

1.需求 基本每个移动端组件库都有组件 icon组件 图标组件、 但是很多组件库中并找不到我们需要的图标 这时候 大家有可能会找图标库 最大众的就是iconfont的图标了 2.使用 有很多方式去使用这个东西 比如将再限链接中的css引入 在使用 直接下载图标 symbol 方式 等....…

【NR 定位】3GPP NR Positioning 5G定位标准解读(十三)-DL-AoD定位

前言 3GPP NR Positioning 5G定位标准&#xff1a;3GPP TS 38.305 V18 3GPP 标准网址&#xff1a;Directory Listing /ftp/ 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;一&#xff09;-CSDN博客 【NR 定位】3GPP NR Positioning 5G定位标准解读&#xff08;…

buuctf warmup 超详细

目录 1.代码审计&#xff1a; 2.逻辑分析 3.总结分析 4.分析记录 5.疑点解答 1.代码审计&#xff1a; <?phphighlight_file(__FILE__);class emmm //定义了一个类{public static function checkFile(&$page) 类里面又申明创建…

Android 架构师研发技术进阶之路:不同阶段需要掌握的那些技术及软技能

资深 而到了资深层次&#xff0c;技术栈已经不再是阻碍。能够从更高层面看待问题&#xff0c;理解整个系统的设计&#xff0c;作为系统架构师的角色存在。 1. 理解微服务、SOA思想&#xff0c;对于后端开发有一定涉猎。 2. 了解前端研发工具和思想&#xff0c;知道vue react…

centos破解root密码以及如何防止他人破解root密码

目录 破解root密码 服务器重启 1.再重启页面上下选择第一个按e进入内核编辑模式 2.找到linux16开头的一行&#xff0c;光标移动到最后添加 init/bin/sh Ctrlx 保存 3.进入单用户模式 4.重新挂在根分区 5.关闭selinux 6.更新密码 passwd 7.在根分区下面创建一个隐藏文件…

【C语言步行梯】一维数组、二维数组介绍与应用详谈

&#x1f3af;每日努力一点点&#xff0c;技术进步看得见 &#x1f3e0;专栏介绍&#xff1a;【C语言步行梯】专栏用于介绍C语言相关内容&#xff0c;每篇文章将通过图片代码片段网络相关题目的方式编写&#xff0c;欢迎订阅~~ 文章目录 为什么要有数组&#xff1f;一维数组数组…

uni-app微信小程序上拉加载,下拉刷新

pages.json配置官网链接 onPullDownRefresh、onReachBottom函数跟生命周期同级 data() {return {orderList:[],total: null, //总共多少条数据page: 1,pageSize: 10,} }, onLoad() {}, mounted(){this.getInfo() }, methods:{getInfo(){API.getListxxx().then(res > {const…

探索TikTok云手机在社交媒体营销的作用

近年来&#xff0c;TikTok作为全球短视频平台之一&#xff0c;其用户基数呈现持续增长的趋势。伴随社交媒体的蓬勃发展&#xff0c;企业和个人纷纷涌入TikTok平台&#xff0c;追求更广泛的曝光和用户互动。为满足这一需求&#xff0c;TikTok云手机应运而生。本文将深度剖析TikT…

蓝桥杯[OJ 1621]挑选子串-CPP-双指针

目录 一、题目描述&#xff1a; 二、整体思路&#xff1a; 三、代码&#xff1a; 一、题目描述&#xff1a; 二、整体思路&#xff1a; 要找子串&#xff0c;则必须找头找尾&#xff0c;找头可以遍历连续字串&#xff0c;找尾则是要从头的基础上往后遍历&#xff0c;可以设头…

【JS逆向学习】猿人学第六题 js混淆 回溯

逆向目标 网址&#xff1a;https://match.yuanrenxue.cn/match/6接口&#xff1a;https://match.yuanrenxue.cn/api/match/6参数&#xff1a;payload(m、q) 逆向过程 老规矩&#xff0c;先来分析网络请求&#xff0c;加密的地方一目了然&#xff0c;没什么可多说的&#xff…

【阿里云系列】-部署ACK集群的POD应用日志如何集成到日志服务(SLS)中

介绍 我们在实际部署应用到阿里云的ACK集群后&#xff0c;由于后期应用服务的持续维护诉求可能需要跟踪排查问题&#xff0c;此时就要具备将应用的历史日志存档便于后期排查问题 处理方式 为了解决以上的普遍需求&#xff0c;需要将ACK中的应用日志采集到SLS的Logstore中,然…

通付盾Web3专题 | SharkTeam:2023年加密货币犯罪分析报告

2023年&#xff0c;Web3行业共经历了940多起大大小小的安全事件&#xff0c;同比2022年增长了超过50%&#xff0c;损失金额达到17.9亿美元。其中&#xff0c;第三季度发生的安全事件最多&#xff08;360起&#xff09;&#xff0c;损失最大&#xff08;7.4亿美元&#xff09;&a…

数据集成平台选型建议

一 数据集成介绍 数据集成平台是一种用于管理和协调数据流动的软件工具或服务。它的主要目标是将来自多个不同数据源的数据整合到一个统一的、易于访问和分析的数据存储库中。这些数据源可以包括数据库、云应用、传感器、日志文件、社交媒体等等。数据集成平台的关键任务是确保…

[JavaWeb学习日记]Vue工程,springboot工程整合Mybatis,数据库索引

目录 一.Vue工程 安装NodeJS与Vue-cli Vue项目创建 启动Vue项目&#xff1a;点击npm脚本serve 改端口&#xff1a;在vue.config.js下 Vue文件组成&#xff1a;templatescriptstyle 使用element 前端服务器当前使用Ngix 主要编写的文件 二.SpringBoot的Web工程 启动带…

腾讯云4核8G服务器支持多少人在线?CPU性能如何?

腾讯云轻量4核8G12M服务器配置446元一年&#xff0c;646元12个月&#xff0c;腾讯云轻量应用服务器具有100%CPU性能&#xff0c;系统盘为180GB SSD盘&#xff0c;12M带宽下载速度1536KB/秒&#xff0c;月流量2000GB&#xff0c;折合每天66.6GB流量&#xff0c;超出月流量包的流…

uniapp-vue3 项目初始化集成配置【开箱即用】

地址 https://gitee.com/charrie/vue3-uniapp-init 部分截图展示 技术说明 采用vue3viteuniapp技术栈&#xff0c;setup语法糖编码方式使用完全免费的sard-uniapp组件库引入unocss量子化样式引擎&#xff0c;动态css不用自己写样式&#xff0c;引用class即可&#xff0c;降低…

【SQL注入】Sqlmap使用指南(手把手保姆版)持续更新

文章目录 一、sqlmap介绍二、sqlmap命令行参数用法讲解2.1常用用法-u--batch--flush-session--dbms--level--random-agent--user-agent--tamper--technique-p--skip基础用法查询列表2.2 高阶用法-v高阶用法查询列表 一、sqlmap介绍 官网下载地址&#xff1a;https://github.co…

【LeetCode热题100】138. 随机链表的复制(链表)

一.题目要求 给你一个长度为 n 的链表&#xff0c;每个节点包含一个额外增加的随机指针 random &#xff0c;该指针可以指向链表中的任何节点或空节点。 构造这个链表的 深拷贝。 深拷贝应该正好由 n 个 全新 节点组成&#xff0c;其中每个新节点的值都设为其对应的原节点的值…

微信小程序H5设置全局弹窗

微信小程序&H5设置全局弹窗 微信小程序&H5设置全局弹窗效果图1、下载所需库2、创建vue.config.js 文件3、创建全局公告组件头部公告组件弹窗公告组件4、组件注册到全局5、在pages.json文件中配置 insetLoader6、H5需要额外使用render.js7、全局调用(一进入页面就获取弹…

18 OpenCV霍夫变换检测直线

文章目录 HoughLines 算子HoughLinesP 算子示例 HoughLines 算子 cv::HoughLines( InputArray src, // 输入图像&#xff0c;必须8-bit的灰度图像 OutputArray lines, // 输出的极坐标来表示直线 double rho, // 生成极坐标时候的像素扫描步长 double theta, //生成极坐标时候…