仿函数(也叫函数对象 )是C++中一种重载****了 () 运算符的类/结构体,它看起来像函数调用,却拥有类的特性(可以保存状态、支持模板、可被编译器优化),常配合STL算法(如 sort 、 lower_bound )使用,比普通函数/函数指针更灵活。
一、仿函数的核心特点
- 本质是类对象: 重载 operator() 后,可像函数一样调用;
- 可保存状态:类的成员变量可以存储上下文信息(普通函数做不到);
- 编译器优化友好:比函数指针更容易被编译器内联,效率更高;
- 支持模板:可以编写通用的仿函数,适配不同类型。
二、基础示例:简单仿函数
- 无状态仿函数(最基础)
重载 operator() ,实现简单的运算/判断逻辑:
cpp
#include <iostream>
using namespace std;
// 定义仿函数:加法仿函数
struct Add
{
// 重载()运算符,参数为两个int,返回和
int operator()(int a, int b) const
{
return a + b;
}
};
// 定义仿函数:判断是否为偶数
struct IsEven
{
bool operator()(int num) const
{
return num % 2 == 0;
}
};
int main()
{
// 1. 创建仿函数对象,像调用函数一样使用
Add add_func;
cout << "3 + 5 = " << add_func(3, 5) << endl; // 输出:8
// 2. 也可以直接创建临时对象调用
cout << "7 + 2 = " << Add()(7, 2) << endl; // 输出:9
// 3. 偶数判断仿函数
IsEven is_even;
cout << "6是否为偶数:" << boolalpha << is_even(6) << endl; // 输出:true
cout << "5是否为偶数:" << boolalpha << is_even(5) << endl; // 输出:false
return 0;
}
- 有状态仿函数(核心优势)
仿函数可以通过成员变量保存状态,这是普通函数无法实现的:
cpp
#include <iostream>
using namespace std;
// 仿函数:累加器,保存累加的结果
struct Accumulator
{
// 成员变量:保存累加状态
int sum = 0;
// 重载(),每次调用累加一个数
void operator()(int num)
{
sum += num;
cout << "当前累加值:" << sum << endl;
}
};
int main()
{
Accumulator acc;
// 多次调用,状态会持续保存
acc(1); // sum=1
acc(2); // sum=3
acc(3); // sum=6
acc(4); // sum=10
// 访问仿函数的状态
cout << "最终累加结果:" << acc.sum << endl; // 输出:10
return 0;
}
三、仿函数的核心应用:配合STL算法
仿函数是STL算法的"灵魂",常用于自定义排序、查找、遍历规则,这里举几个高频场景:
- 配合 sort 实现自定义排序
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数:降序排序
struct Greater
{
bool operator()(int a, int b) const
{
return a > b;
}
};
// 仿函数:按字符串长度排序
struct StrLenCompare
{
bool operator()(const string& s1, const string& s2) const
{
return s1.size() < s2.size();
}
};
int main()
{
// 1. 整数降序排序
vector<int> nums = {3, 1, 4, 2, 5};
sort(nums.begin(), nums.end(), Greater()); // 传入仿函数对象
for (int num : nums) cout << num << " "; // 输出:5 4 3 2 1
cout << endl;
// 2. 字符串按长度升序排序
vector<string> strs = {"apple", "cat", "banana", "dog"};
sort(strs.begin(), strs.end(), StrLenCompare());
for (const string& s : strs) cout << s << " "; // 输出:cat dog apple banana
cout << endl;
return 0;
}
- 配合 lower_bound 实现自定义查找(降序序列)
结合之前聊的 lower_bound ,用仿函数实现降序序列的二分查找:
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main()
{
// 降序序列
vector<int> vec = {9, 7, 5, 3, 3, 3, 1};
int target = 3;
// greater<int>() 是STL自带的仿函数,实现a > b的比较
auto lower_it = lower_bound(vec.begin(), vec.end(), target, greater<int>());
cout << "lower_bound找到的位置:" << lower_it - vec.begin() << endl; // 输出:3
cout << "对应元素:" << *lower_it << endl; // 输出:3
return 0;
}
- 配合 for_each 遍历并处理元素
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
// 仿函数:打印元素并计数
struct PrintAndCount
{
int count = 0;
void operator()(int num)
{
cout << num << " ";
count++;
}
};
int main()
{
vector<int> nums = {1, 2, 3, 4, 5};
// for_each返回仿函数对象,可获取其状态
PrintAndCount pac = for_each(nums.begin(), nums.end(), PrintAndCount());
cout << endl << "遍历的元素个数:" << pac.count << endl; // 输出:5
return 0;
}
四、STL自带的仿函数
C++标准库在 中提供了常用的仿函数,无需自己定义,直接使用:
| 仿函数 | 功能 | 示例 |
|---|---|---|
| 加法(如std::plus) | 执行两个参数的加法运算 | std::plus()(3,5)→8;(或自定义Add: [](int a,int b){return a+b;}) |
| 减法(如std::minus) | 执行两个参数的减法运算 | std::minus()(10,4)→6;(或自定义Sub: [](int a,int b){return a-b;}) |
| 大于比较(如std::greater) | 判断第一个参数是否大于第二个参数 | std::greater()(7,3)→true;(或自定义Greater: [](int a,int b){return a>b;}) |
| 小于比较(如std::less) | 判断第一个参数是否小于第二个参数 | std::less()(2,9)→true;(或自定义Less: [](int a,int b){return a<b;}) |
| 等于比较(如std::equal_to) | 判断两个参数是否相等 | std::equal_to()(5,5)→true;(或自定义Equal: [](int a,int b){return a==b;}) |
| 示例: |
cpp
#include <iostream>
#include <functional>
using namespace std;
int main()
{
plus<int> add;
cout << "2 + 6 = " << add(2, 6) << endl; // 输出:8
greater<int> gt;
cout << "5 > 3 ? " << boolalpha << gt(5, 3) << endl; // 输出:true
return 0;
}
五、仿函数与普通函数的区别
| 特性 | 仿函数 | 普通函数 |
|---|---|---|
| 保存状态 | 支持(通过成员变量保存状态) | 不支持(仅能用全局变量间接保存) |
| 编译器优化 | 易内联,执行效率高 | 函数指针调用不易被编译器优化 |
| 模板适配 | 天然支持模板(可作为模板参数) | 需单独定义模板函数或使用函数指针包装 |
| 灵活性 | 高(可自定义成员变量、成员函数) | 低(仅能通过函数逻辑实现固定行为) |