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

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

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

一、什么是仿函数?

1. 核心定义

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

简单说:

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

创建这个类的对象;

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

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

2. 最简示例

先看一个最直观的例子:

cpp 复制代码
#include <iostream>
using namespace std;

// 定义一个仿函数类
class MyFunctor {
public:
    // 重载 () 运算符 → 核心!
    void operator()() {
        cout << "我是仿函数!" << endl;
    }
};

int main() {
    MyFunctor func;  // 创建对象
    func();          // 像函数一样调用!本质是 func.operator()()
    return 0;
}

运行输出:

cpp 复制代码
我是仿函数!

3. 带参数的仿函数

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

cpp 复制代码
// 加法仿函数
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. 仿函数最大优势:自带 "记忆"(状态)

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

示例:计数器仿函数

cpp 复制代码
// 计数仿函数:记录自己被调用了多少次
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;
}

输出:

cpp 复制代码
1
2
1

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

2. 三者对比

表格

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

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

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

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

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

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

cpp 复制代码
#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;
}

输出:

cpp 复制代码
5 4 3 1 1
  1. 配合 for_each 遍历处理
cpp 复制代码
// 打印仿函数
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>:取反

示例:

cpp 复制代码
#include <functional>
plus<int> p;
cout << p(10, 20) << endl;  // 30

2. 关系仿函数

equal_to<T>:等于

greater<T>:大于

less<T>:小于

常用于排序:

cpp 复制代码
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++ 面向对象 + 泛型编程的经典结合,掌握它能大幅提升代码的灵活性和复用性。

相关推荐
萝卜白菜。9 分钟前
TongWeb7.0相同的类指明加载顺序
开发语言·python·pycharm
绛橘色的日落(。・∀・)ノ9 分钟前
Matplotlib实践学习笔记
笔记·学习
wb043072019 分钟前
使用 Java 开发 MCP 服务并发布到 Maven 中央仓库完整指南
java·开发语言·spring boot·ai·maven
chase。10 分钟前
【学习笔记】AGILE:把人形机器人强化学习从“玄学”变成“工程学”
笔记·学习·敏捷流程
Rsun0455110 分钟前
设计模式应该怎么学
java·开发语言·设计模式
久菜盒子工作室17 分钟前
高等教育学|第一章高等教育概述
经验分享·笔记·课程设计
Tanecious.20 分钟前
蓝桥杯备赛:Day3-P1918 保龄球
c++·蓝桥杯
良木生香27 分钟前
【C++初阶】:C++类和对象(下):构造函数promax & 类型转换 & static & 友元 & 内部类 & 匿名对象 & 超级优化
c语言·开发语言·c++
5系暗夜孤魂33 分钟前
系统越复杂,越需要“边界感”:从 Java 体系理解大型工程的可维护性本质
java·开发语言
tq10861 小时前
语言流形与思维共生:中西认知图景的差异与交融
笔记