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

相关推荐
daanpdf40 分钟前
新视野大学英语视听说教程2第四版听力音频原文及答案
笔记
Halo_tjn1 小时前
Java 基于字符串相关知识点
java·开发语言·算法
梦想的颜色1 小时前
java 利用redis来限制用户频繁点击
java·开发语言
报错小能手1 小时前
Swift 并发 Combine响应式框架
开发语言·ios·swift
万法若空2 小时前
C++ <memory> 库全方位详解
开发语言·c++
代码中介商2 小时前
C++ 类型转换深度解析:static_cast、dynamic_cast、const_cast、reinterpret_cast
开发语言·c++
青小莫2 小时前
C++之string(OJ练习)
开发语言·c++·stl
freshman_y2 小时前
一篇介绍C语言中二级指针和二维数组的文章
c语言·开发语言
6Hzlia2 小时前
【Hot 100 刷题计划】 LeetCode 199. 二叉树的右视图 | C++ DFS 逆序遍历
c++·leetcode·深度优先
-Marks-2 小时前
【C++编程】STL简介 --- (是什么 | 版本发展历程 | 六大组件 | 重要性缺陷以及如何学习)
开发语言·c++·学习·stl·stl版本