在 C++ 中,仿函数(Functor) 是一种特殊的对象,它通过重载 operator()
运算符使得类的实例可以像函数一样被调用。仿函数结合了面向对象和函数式编程的特点,常用于 STL 算法、回调机制和策略模式中。
核心概念
-
本质
仿函数是一个类或结构体,通过定义operator()
运算符,使对象可以像函数一样被调用。 -
优势
- 状态保持:可以保存内部状态(普通函数无法做到)。
- 灵活性:可作为参数传递,比函数指针更安全、更高效(支持内联优化)。
- 模板友好:泛型编程中兼容性更好。
基本实现
#include <iostream>
// 定义一个仿函数类
class Adder {
public:
Adder(int value) : increment(value) {}
// 重载 operator()
int operator()(int x) const {
return x + increment;
}
private:
int increment;
};
int main() {
Adder add5(5); // 创建一个仿函数对象,每次加5
std::cout << add5(10); // 输出 15(调用 add5.operator()(10))
return 0;
}
仿函数 vs 函数指针
特性 | 仿函数 | 函数指针 |
---|---|---|
状态保持 | ✔️ 可以保存成员变量 | ❌ 无法保持状态 |
内联优化 | ✔️ 编译器可能内联 | ❌ 通常无法内联 |
泛型编程 | ✔️ 类型安全,适用于模板 | ❌ 类型转换可能不安全 |
灵活性 | ✔️ 可定义多种操作的重载 | ❌ 只能指向固定签名的函数 |
应用场景
-
STL 算法
仿函数广泛用于 STL 算法的自定义行为,例如排序、查找、变换等。#include <algorithm> #include <vector> struct Compare { bool operator()(int a, int b) const { return a > b; // 降序排序 } }; int main() { std::vector<int> nums = {3, 1, 4, 1, 5}; std::sort(nums.begin(), nums.end(), Compare()); return 0; }
-
策略模式
通过传递不同的仿函数对象,动态改变算法行为。template <typename Strategy> void processData(Strategy strategy) { // 使用策略处理数据 strategy.apply(); } class FastStrategy { public: void apply() { /* 快速处理逻辑 */ } }; class SafeStrategy { public: void apply() { /* 安全处理逻辑 */ } };
-
状态保持
保存多次调用的中间状态。class Counter { public: Counter() : count(0) {} int operator()() { return ++count; } private: int count; }; Counter cnt; cnt(); // 返回1 cnt(); // 返回2
C++11 后的增强
-
Lambda 表达式
Lambda 本质上是匿名仿函数,语法更简洁:auto add5 = [value=5](int x) { return x + value; }; std::cout << add5(10); // 输出15
-
std::function
通用函数包装器,可存储仿函数、Lambda、函数指针等:#include <functional> std::function<int(int)> func = add5; std::cout << func(10); // 输出15
总结
- 仿函数是 C++ 中强大的工具,结合了对象和函数的特性。
- 在需要状态保持、模板编程或策略模式时,优先考虑仿函数。
- 现代 C++ 中,Lambda 和
std::function
提供了更简洁的替代方案,但底层原理一致。