C++的仿函数functor
详细内容
仿函数(Functor)是 C++ 中的一种设计模式,也叫函数对象。仿函数是一个重载了 operator()
的类或结构体,它可以像普通函数一样被调用。这使得它具有类似函数的行为,但实际上它是一个对象,因此可以拥有状态(成员变量)和更多的灵活性。
仿函数的主要用途是:
- 可以像函数一样调用,并且可以存储状态。
- 可以将仿函数对象传递给算法(如
std::sort
)来代替普通函数或函数指针。 - 可以实现更复杂的逻辑,且在多次调用时可以保持内部状态。
1. 仿函数的基本示例
一个简单的仿函数可以是这样:
#include <iostream>
// 定义一个仿函数类
struct Add {
// 重载 operator() 使其成为仿函数
int operator()(int a, int b) const {
return a + b;
}
};
int main() {
Add add; // 创建仿函数对象
int result = add(3, 4); // 像调用函数一样调用仿函数
std::cout << "Result: " << result << std::endl; // 输出 7
return 0;
}
在这个例子中,Add
是一个仿函数类,它重载了 operator()
,因此我们可以像调用函数一样使用 Add
对象。
2. 仿函数的灵活性:保持状态
与普通函数不同,仿函数是类的实例,它可以保存状态。你可以通过成员变量来存储状态,并在 operator()
函数中使用这些状态。
例如,定义一个带有状态的仿函数:
#include <iostream>
// 带有状态的仿函数
struct Multiplier {
int factor; // 状态变量
// 构造函数,用于设置初始状态
Multiplier(int f) : factor(f) {}
// 重载 operator(),可以访问成员变量 factor
int operator()(int x) const {
return x * factor;
}
};
int main() {
Multiplier timesTwo(2); // 创建一个状态为 2 的仿函数对象
Multiplier timesThree(3); // 创建一个状态为 3 的仿函数对象
std::cout << "2 * 5 = " << timesTwo(5) << std::endl; // 输出 10
std::cout << "3 * 5 = " << timesThree(5) << std::endl; // 输出 15
return 0;
}
在这个例子中,Multiplier
仿函数对象包含一个状态 factor
,它可以在调用时影响运算结果。
3. 仿函数与标准算法
仿函数非常适合与 C++ 标准库中的算法配合使用。例如,可以将仿函数对象作为参数传递给 std::sort
等算法,而不是函数指针或 lambda 表达式。
例如,使用仿函数自定义排序:
#include <iostream>
#include <vector>
#include <algorithm>
// 定义一个仿函数,用于降序比较
struct CompareDesc {
bool operator()(int a, int b) const {
return a > b;
}
};
int main() {
std::vector<int> vec = {3, 1, 4, 1, 5, 9, 2};
// 使用仿函数进行降序排序
std::sort(vec.begin(), vec.end(), CompareDesc());
std::cout << "Sorted in descending order: ";
for (int n : vec) {
std::cout << n << " ";
}
std::cout << std::endl;
return 0;
}
在这个例子中,CompareDesc
是一个仿函数,它用于自定义比较规则(降序排序),并传递给 std::sort
。
4. 仿函数的优势
- 状态存储:与普通函数不同,仿函数可以通过类的成员变量保存状态,这使得仿函数更适合实现复杂的逻辑或多次调用时需要保存状态的情况。
- 可组合性:仿函数可以在创建时初始化状态,并且可以像其他对象一样复制、传递和保存。
- 高效:在某些情况下,仿函数的性能可能比
std::function
更好,因为仿函数没有类型擦除和动态分配的开销。
5. 仿函数与 std::function
的对比
- 仿函数 是编译期决定的,它是一个类,编译器知道它的确切类型,并且可以进行内联优化。
std::function
是一个通用的可调用对象包装器,它可以持有任何类型的可调用对象(包括普通函数、lambda、仿函数等),但有一些运行时开销(如类型擦除和动态分配)。
仿函数通常用于需要最大化性能并且不需要 std::function
的灵活性时。
总结:
- 仿函数是一个重载了
operator()
的类或结构体,能够像普通函数一样被调用。 - 仿函数可以存储状态,非常适合需要在多次调用时保留状态的场景。
- 仿函数可以与标准库算法配合使用,如
std::sort
,用于自定义比较规则等。 - 与
std::function
相比,仿函数更加高效,但不如std::function
灵活。