当前位置: 首页 > news >正文

C++ 笔记 仿函数(函数对象)

在 C++ 编程中,仿函数(Functor)也叫函数对象(Function Object),是一个披着类 / 对象外衣、却能像普通函数一样被调用的语法特性。它是 STL(标准模板库)的核心设计之一,既能实现函数的功能,又拥有对象的状态、封装、继承等优势,是 C++ 区别于其他语言的实用编程技巧。

本文从基础定义、用法、优势、实战场景完整讲解仿函数,帮你快速掌握这个高频知识点。

一、什么是仿函数?

1. 核心定义

仿函数 = 重载了operator()运算符的类的对象

简单说:

写一个类,在里面重载()运算符;

创建这个类的对象;

这个对象就可以像函数一样使用对象名(参数)调用

因为 “用起来像函数”,所以叫仿函数

2. 最简示例

先看一个最直观的例子:

#include <iostream> using namespace std; // 定义一个仿函数类 class MyFunctor { public: // 重载 () 运算符 → 核心! void operator()() { cout << "我是仿函数!" << endl; } }; int main() { MyFunctor func; // 创建对象 func(); // 像函数一样调用!本质是 func.operator()() return 0; }

运行输出:

我是仿函数!

3. 带参数的仿函数

仿函数可以像普通函数一样接收参数、有返回值:

// 加法仿函数 class Add { public: int operator()(int a, int b) { return a + b; } }; int main() { Add add; int res = add(10, 20); // 像函数一样传参调用 cout << res << endl; // 输出 30 return 0; }

二、仿函数 vs 普通函数 vs Lambda

你一定会问:既然有函数,为什么还要仿函数?

这是仿函数最核心的价值:仿函数是对象,可以保存状态

1. 仿函数最大优势:自带 “记忆”(状态)

普通函数用静态变量也能存状态,但线程不安全、代码混乱;仿函数用成员变量存状态,安全、干净、灵活

示例:计数器仿函数

// 计数仿函数:记录自己被调用了多少次 class Counter { private: int count = 0; // 成员变量 → 状态 public: int operator()() { return ++count; } }; int main() { Counter c1, c2; // 两个独立对象,各自计数 cout << c1() << endl; // 1 cout << c1() << endl; // 2 cout << c2() << endl; // 1 return 0; }

输出:

1 2 1

两个对象互不干扰,完美实现带独立状态的函数

2. 三者对比

表格

特性普通函数仿函数(函数对象)Lambda 表达式
能否保存状态难(静态变量不安全)能(成员变量)能(捕获变量)
能否复用 / 继承不能不能
作为 STL 算法参数可以推荐可以
代码复杂度简单稍复杂简洁

结论:需要带状态、可复用、可配置的函数逻辑时,仿函数是最优解。

三、仿函数在 STL 中的实战用法

STL 大量算法(sortfind_iffor_each等)都依赖仿函数作为策略参数,这是仿函数最常用的场景。

1. 配合 sort 实现自定义排序

默认sort是升序,用仿函数可以轻松改规则:

#include <iostream> #include <vector> #include <algorithm> using namespace std; // 降序仿函数 class GreaterThan { public: bool operator()(int a, int b) { return a > b; } }; int main() { vector<int> v = {3,1,4,1,5}; sort(v.begin(), v.end(), GreaterThan()); // 传入仿函数对象 for(int x : v) cout << x << " "; return 0; }

输出:

5 4 3 1 1

2. 配合 for_each 遍历处理

// 打印仿函数 class Print { public: void operator()(int x) { cout << x << " "; } }; // 使用 for_each(v.begin(), v.end(), Print());

四、仿函数的分类(STL 标准)

C++ STL 内置了大量标准仿函数,放在<functional>头文件中,分为三类:

1. 算术仿函数

plus<T>:加法

minus<T>:减法

multiplies<T>:乘法

negate<T>:取反

示例:

#include <functional> plus<int> p; cout << p(10, 20) << endl; // 30

2. 关系仿函数

equal_to<T>:等于

greater<T>:大于

less<T>:小于

常用于排序:

sort(v.begin(), v.end(), greater<int>()); // 降序

3. 逻辑仿函数

logical_and<T>:逻辑与

logical_or<T>:逻辑或

logical_not<T>:逻辑非

五、仿函数底层原理(一句话)

仿函数本质是对象调用重载的operator(),编译器会把func(a,b)直接翻译成func.operator()(a,b)

它的效率极高,和普通函数几乎没有性能差距,甚至比函数指针更快。

六、使用注意事项

  1. 仿函数必须重载()运算符,这是唯一入口;
  2. 仿函数对象可以临时创建(直接写类名());
  3. 成员变量可以给仿函数设置初始状态
  4. STL 算法传参时,优先用仿函数 / Lambda,少用函数指针;
  5. 现代 C++ 中,简单场景可用Lambda替代仿函数,但复杂带状态逻辑仍推荐仿函数。

总结

  1. 仿函数:重载operator()的类的对象,用起来像函数
  2. 核心优势:可以用成员变量保存状态,比普通函数更强大;
  3. 主要用途:作为 STL 算法的策略(排序、过滤、遍历);
  4. 语法返回值 operator()(参数) { 实现 }
  5. STL 常用greater<int>()less<int>()plus<int>()

仿函数是 C++ 面向对象 + 泛型编程的经典结合,掌握它能大幅提升代码的灵活性和复用性。

http://www.jsqmd.com/news/578150/

相关文章:

  • 2024年Image Caption数据集全攻略:从COCO到TextCaps的实战选择指南
  • Blazor中的日期选择与绑定问题
  • 微信支付ApiV3回调实战:Java版签名校验与参数解密全流程解析
  • 2026年做得好的商务商业计划书代写机构推荐,值得一看!消费品市场调研报告/商业合作计划书,商业计划书代写机构有哪些 - 品牌推荐师
  • 深度学习YOLOv8+Pyqt5:实时监测与精准识别吸烟行为的系统解决方案
  • 如何用ABAP代码下载SE78上传的图片?附完整源码解析
  • FinalShell在Ubuntu上的替代方案:当远程桌面不可用时该怎么办?
  • 2026年上海口腔诊疗机构参考指南:华齿口腔、上海种植牙、牙齿正畸、口腔修复、上海口腔医院,以专业守护口腔健康 - 海棠依旧大
  • 2026届毕业生推荐的十大降重复率助手实际效果
  • 从Ollama版本到磁盘空间:全面排查Qwen3:32b模型加载失败的N种可能
  • 别光看引脚!手把手教你用STM32CubeMX配置RMII以太网(附时钟源选择避坑)
  • deepseekv4为什么一直未发布?
  • 用MATLAB搞定模电实验:单管共射放大电路静态工作点与放大倍数的保姆级仿真
  • 3步解锁音乐自由:QMCDecode让Mac用户告别格式困扰
  • 别再被‘域名解析错误’骗了!深度拆解Dify离线部署时工作流迁移的真实兼容性问题与修复方案
  • 新手避坑指南:用STM32F103C8T6+OLED+DS18B20+DHT11复刻智能万年历(附完整代码)
  • 2026年麻辣烫加盟优质品牌参考:汆悦麻辣烫、小鲜骨汤、黏糊双酱、东北老味、红油、番茄、红酸汤以多元口味与全链支撑助力餐饮创业 - 海棠依旧大
  • 华为S5700交换机SSH与TELNET双协议远程管理配置全指南
  • 高效清理Windows运行命令历史记录的4种实用方法
  • 使用nvm轻松管理多版本Node.js开发环境
  • 9 鸿蒙页面渲染效率优化实战 | 鸿蒙开发筑基实战
  • STM32F407 ADC实战:从CubeMX配置到高精度电压采集
  • 从信号处理看StyleGAN3:为什么传统GAN会生成‘粘性‘纹理?
  • 2026届毕业生推荐的AI论文工具实际效果
  • 实战指南 | 将SEAM注意力机制集成到YOLOv8,提升遮挡目标检测性能
  • 避坑指南:STM32G070 ADC多通道+DMA配置,这几个CubeMX设置项千万别搞错
  • PP-DocLayoutV3部署教程:/root/ai-models路径优先加载机制深度解析
  • 避坑指南:桌面机械臂总线舵机模式设置与单关节控制常见问题排查
  • 锐捷交换机系统升级避坑指南:MGMT口与普通接口的差异解析
  • 如何让Windows直接运行APK?轻量级跨平台效率工具的创新实践