探索仿函数(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算法,以便按照升序对整数进行排序。

⚠️我们如果要使用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.mfbz.cn/a/459249.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AWS云上面的k8s统一日志收集(Fluent Bit+EKS+CW)

目标 k8s上面的常见的统一日志方案是EFK&#xff0c;具体如下&#xff1a; E:elasticsearch;F:fluentd;K:kibana 这里我们变成了使用fluentd的AWS替代品Fluent Bit&#xff0c;直接将日志输出到CloudWatch组。不需要E和K了。不过&#xff0c;这样仅仅用于AWS EKS。 步骤 给…

【C语言】字符与字符串---从入门到入土级详解

&#x1f984;个人主页:修修修也 &#x1f38f;所属专栏:C语言 ⚙️操作环境:Visual Studio 2022 目录 一.字符类型和字符数组&#xff08;串&#xff09;简介 1.ASCII 2.定义&#xff0c;初始化&#xff0c;使用 1>字符的定义及初始化 2>字符串的定义及初始化 二.…

Linux——信号量

目录 POSIX信号量 信号量原理 信号量概念 信号量函数 基于环形队列的生产者消费者模型 生产者和消费者申请和释放资源 单生产者单消费者 多生产者多消费者 多生产者多消费者的意义 信号量的意义 POSIX信号量 信号量原理 如果仅用一个互斥锁对临界资源进行保护&#…

19.ADC模数转换器知识点+AD单通道AD多通道应用程序示例

0. 江协科技/江科大-STM32标准库开发-各章节详细笔记-查阅传送门_江协科技stm32笔记-CSDN博客文章浏览阅读2.9k次&#xff0c;点赞44次&#xff0c;收藏128次。江协科技/江科大-STM32标准库开发-各章节详细笔记-传送门至各个章节笔记。基本上课程讲的每句都详细记录&#xff0c…

PHP微信朋友圈广告植入源码 – 提供高效的广告植入解决方案,助力微信朋友圈广告推广

内容介绍 管理后台 可以无限制帮用户开户&#xff0c;也可以理解为多用户版。 可以管理用户发布文章条数&#xff0c;也可以无限制发布。 前端用户 用户可以上传多个广告&#xff0c;每个广告分别进行统计展示及点击。 用户一键植入&#xff0c;不用粘贴网址&#xff0c;每篇…

Spring Security自定义认证授权过滤器

自定义认证授权过滤器 自定义认证授权过滤器1、SpringSecurity内置认证流程2、自定义Security认证过滤器2.1 自定义认证过滤器2.2 定义获取用户详情服务bean2.3 定义SecurityConfig类2.4 自定义认证流程测试 3、 基于JWT实现无状态认证3.1 认证成功响应JWT实现3.2 SpringSecuri…

从大厂到高校,鸿蒙人才“红透半边天”!

截至目前&#xff0c;继清华大学、北京航空航天大学、武汉大学等985高校开设鸿蒙相关课程后&#xff0c;已经或将要开设鸿蒙相关课程的985、211高校达到近百所&#xff0c;为鸿蒙人才培养提供沃土。 随着鸿蒙系统即将摒弃安卓&#xff0c;鸿蒙原生应用将全面启动的背景下&…

4款实用性前端动画特效分享(附在线演示)

分享4款非常不错的项目动画特效 其中有jQuery特效、canvas特效、CSS动画等等 下方效果图可能不是特别的生动 那么你可以点击在线预览进行查看相应的动画特效 同时也是可以下载该资源的 全屏图片视差旋转切换特效 基于anime.js制作全屏响应式的图片元素布局&#xff0c;通过左…

javaweb学习(day09-Web开发会话技术)

一、会话 1 基本介绍 1.1 什么是会话&#xff1f; 会话可简单理解为&#xff1a;用户开一个浏览器&#xff0c;点击多个超链接&#xff0c;访问服务器多个 web 资源&#xff0c;然 后关闭浏览器&#xff0c;整个过程称之为一个会话。 1.2 会话过程中要解决的一些问题&#…

[LVGL]:MACOS下使用LVGL模拟器

如何在MACOS下使用lvgl模拟器 1.安装必要环境 brew install sdl2查看sdl2安装位置&#xff1a; (base) ➜ ~ brew list sdl2 /opt/homebrew/Cellar/sdl2/2.30.1/bin/sdl2-config /opt/homebrew/Cellar/sdl2/2.30.1/include/SDL2/ (78 files) /opt/homebrew/Cellar/sdl2/2.3…

权威发布!艾媒咨询最新报告显示65.1%的消费者还在睡硬床

一直以来,“睡硬床好”观念在中国盛行,国民较多青睐硬床,床垫厂商为迎合大众,大批量生产硬床垫。日前,全球新经济产业第三方数据挖掘和分析机构iiMedia Research(艾媒咨询)发布《2024年中国硬床垫使用现状及潜在危害调研》(以下简称:报告),数据显示,75.1%的消费者对“睡硬床对腰…

DM数据库安装及使用(Windows、Linux、docker)

Windows 先解压安装包 点击setup安装 下一步 勾选接受然后下一步 下一步 选择典型安装下一步 下一步 搜索DM数据库配置助手然后一直下一步 然后搜索DM管理工具 登录 登录成功 widows版本安装成功 Linux安装 操作系统CPU数据库CentOS7x86_64 架构dm8_20230418_x86_rh6_64 …

Nacos源码流程图

1.Nacos1.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695eb260d7157a7bc6adb 2.Nacos2.x版本服务注册与发现源码 流程图地址&#xff1a;https://www.processon.com/view/link/634695fb260d7157a7bc6ae0 3.Nacos2.x版本GRPC…

【深度学习笔记】10_11 注意力机制

注&#xff1a;本文为《动手学深度学习》开源内容&#xff0c;部分标注了个人理解&#xff0c;仅为个人学习记录&#xff0c;无抄袭搬运意图 10.11 注意力机制 在10.9节&#xff08;编码器—解码器&#xff08;seq2seq&#xff09;&#xff09;里&#xff0c;解码器在各个时间…

Leet code 1658 将x减到0的最小操作数

解题思路&#xff1a;滑动窗口 主要思想&#xff1a;正难逆简 题目需要左找一个数 右找一个数 我们不如直接找中间最长的一连串子数让这串子树和为 数组子树和减去X 找不到就返回 -1 滑动窗口双指针从左端出发&#xff0c;进行 进窗口 判断 出窗口 更新结果四个步骤 代码…

用机床测头为什么能提升加工中心精度?提高生产效率?

制造业的蓬勃发展为企业提出了更高的精度和效率要求。在现代制造业中&#xff0c;机床测头作为一种关键的检测装置&#xff0c;能够实时监控加工过程中的误差&#xff0c;及时调整&#xff0c;保证加工质量的稳定性&#xff0c;提高加工中心的精度&#xff0c;进而提升生产效率…

QT 如何在QPushButton上播放gif(终极版)

在平时浏览网站&#xff0c;或者使用软件的时候&#xff0c;经常可以见到&#xff1a;在点击了某个按钮之后&#xff0c;按钮上会显示动图以及提示文字。在QT中&#xff0c;比较常见且简单的做法就是&#xff1a;给按钮设置一个layout&#xff0c;然后在这个layout里面添加QLab…

openssl3.2 - note - Getting Started with OpenSSL

文章目录 openssl3.2 - note - Getting Started with OpenSSL概述笔记openssl 历史版本Configure 选项开关支持的OSopenssl 文档简介安装新闻每个平台的安装文档支持的命令列表配置文件格式环境变量 END openssl3.2 - note - Getting Started with OpenSSL 概述 看到官方文档…

三款内衣洗衣机的顶级较量:希亦、小吉、由利,谁才是性价比之王?

洗衣机在我们的生活中可谓是非常常见的了&#xff0c;几乎每家每户都具备着一台。即便是有洗衣机&#xff0c;也有不少人不会将自己我贴身衣物直接扔在洗衣机里清洗&#xff0c;而是会自己手工手洗。这跟我们传统上的观念有很大的关系&#xff0c;认为把内衣、内裤等贴身衣物放…

代码随想录算法训练营三刷day24 | 回溯算法 之 理论基础 77. 组合

三刷day24 理论基础77. 组合递归函数的返回值以及参数回溯函数终止条件单层搜索的过程 理论基础 回溯法解决的问题都可以抽象为树形结构。 因为回溯法解决的都是在集合中递归查找子集&#xff0c;集合的大小就构成了树的宽度&#xff0c;递归的深度&#xff0c;都构成的树的深…