仿函数又称函数对象,本质是重载了operator()运算符的类的对象,可以和普通函数一样被调用。
一、仿函数基础概念
1. 什么是仿函数
仿函数不是函数,而是一个类类型的对象 。当我们给这个对象加上()调用运算符时,它会触发类中重载的operator(),从而实现类似函数的行为。
最简单的仿函数示例:
cpp
#include <iostream>
using namespace std;
// 定义仿函数类
struct Add {
// 重载函数调用运算符
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Add add; // 创建仿函数对象
cout << add(3, 5) << endl; // 像函数一样调用,输出8
cout << Add()(10, 20) << endl; // 临时对象调用,输出30
return 0;
}
2. 仿函数的核心优势(为什么不用普通函数?)
仿函数解决了普通函数和函数指针的诸多局限性,是 STL 首选的可调用对象形式:
| 特性 | 普通函数 | 函数指针 | 仿函数 |
|---|---|---|---|
| 携带状态 | ❌ 只能通过全局变量 | ❌ 只能通过全局变量 | ✅ 可以作为类成员变量存储状态 |
| 性能 | 可内联 | ❌ 间接调用,无法内联 | ✅ 编译器可直接内联operator() |
| 类型安全 | ✅ | ❌ 类型擦除,易出错 | ✅ 每个仿函数都是独立类型 |
| 泛型支持 | 差 | 差 | ✅ 完美适配模板元编程 |
| 可组合性 | 差 | 差 | ✅ 可通过适配器灵活组合 |
3. 仿函数的分类
根据operator()接受的参数个数,仿函数分为:
- 一元仿函数 :接受 1 个参数,如
negate<int> - 二元仿函数 :接受 2 个参数,如
plus<int> - 多元仿函数:C++11 后支持,但 STL 内置仿函数最多二元
二、STL 内置仿函数全解
STL 在<functional>头文件中提供了一套完整的通用仿函数,覆盖了所有基础运算,无需我们重复编写。它们都是模板类,可以适配任意数值类型。
1. 算术运算仿函数
用于执行基本的算术计算,全部是二元仿函数(除了negate是一元)。
| 仿函数 | 功能 | 等价表达式 |
|---|---|---|
plus<T> |
加法 | a + b |
minus<T> |
减法 | a - b |
multiplies<T> |
乘法 | a * b |
divides<T> |
除法 | a / b |
modulus<T> |
取模 | a % b |
negate<T> |
取反(一元) | -a |
使用示例:
cpp
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
vector<int> a = {1, 2, 3, 4};
vector<int> b = {10, 20, 30, 40};
vector<int> c(4);
// 两个数组对应元素相加
transform(a.begin(), a.end(), b.begin(), c.begin(), plus<int>());
// c = {11, 22, 33, 44}
// 所有元素取反
transform(a.begin(), a.end(), a.begin(), negate<int>());
// a = {-1, -2, -3, -4}
return 0;
}
2. 关系运算仿函数
用于比较两个值的大小关系,全部是二元仿函数,返回bool类型。
| 仿函数 | 功能 | 等价表达式 |
|---|---|---|
equal_to<T> |
等于 | a == b |
not_equal_to<T> |
不等于 | a != b |
less<T> |
小于 | a < b |
greater<T> |
大于 | a > b |
less_equal<T> |
小于等于 | a <= b |
greater_equal<T> |
大于等于 | a >= b |
高频使用场景:
cpp
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
vector<int> v = {3, 1, 4, 1, 5, 9};
// 降序排序(默认是less<int>()升序)
sort(v.begin(), v.end(), greater<int>());
// v = {9, 5, 4, 3, 1, 1}
// 统计大于5的元素个数
int cnt = count_if(v.begin(), v.end(), bind(greater<int>(), placeholders::_1, 5));
// cnt = 1
return 0;
}
⚠️ 面试重点 :std::sort默认使用less<T>(),实现升序排列;std::map/std::set默认也使用less<T>作为比较器,保证元素按升序存储。
3. 逻辑运算仿函数
用于执行布尔逻辑运算,返回bool类型。
| 仿函数 | 功能 | 等价表达式 | ||
|---|---|---|---|---|
logical_and<T> |
逻辑与 | a && b |
||
logical_or<T> |
逻辑或 | `a | b` | |
logical_not<T> |
逻辑非(一元) | !a |
使用示例:
cpp
#include <vector>
#include <algorithm>
#include <functional>
using namespace std;
int main() {
vector<int> v = {1, 6, 12, 18, 25};
// 查找大于5且小于20的第一个元素
auto it = find_if(v.begin(), v.end(), [](int x) {
return logical_and<int>()(greater<int>()(x, 5), less<int>()(x, 20));
});
// it指向6
return 0;
}
4. 位运算仿函数(C++11 新增)
用于执行整数的位运算。
| 仿函数 | 功能 | 等价表达式 | |
|---|---|---|---|
bit_and<T> |
按位与 | a & b |
|
bit_or<T> |
按位或 | `a | b` |
bit_xor<T> |
按位异或 | a ^ b |
|
bit_not<T> |
按位取反(C++14) | ~a |