可调用对象(2)-仿函数
仿函数(Functors)
仿函数(Functors),又称函数对象(Function Objects),是在C++中重载了 operator()
的类或结构体实例。仿函数不仅可以像普通函数一样被调用,还能携带状态,提供更大的灵活性和功能性。
定义与使用
仿函数是通过定义一个类或结构体,并重载其调用运算符 operator()
来实现的。
#include <iostream>// 定义一个仿函数类
struct Adder {int to_add;// 构造函数Adder(int value) : to_add(value) {}// 重载()运算符int operator()(int x) const {return x + to_add;}
};int main() {Adder add5(5); // 创建一个添加5的仿函数std::cout << "10 + 5 = " << add5(10) << std::endl; // 输出: 10 + 5 = 15return 0;
}
特点
- 携带状态: 仿函数可以拥有内部状态,通过成员变量存储数据,使其在调用时具备上下文信息。
- 灵活性高: 可以根据需要添加更多的成员函数和变量,扩展功能。
- 性能优化: 编译器可以对仿函数进行优化,例如内联展开,提高执行效率。
高级示例
仿函数不仅可以执行简单的计算,还可以进行复杂的操作。例如,实现一个可变的仿函数,用于累加多个值。
#include <iostream>// 可变累加器仿函数
struct Accumulator {int sum;Accumulator() : sum(0) {}// 重载()运算符void operator()(int x) {sum += x;}
};int main() {Accumulator acc;acc(10);acc(20);acc(30);std::cout << "总和: " << acc.sum << std::endl; // 输出: 总和: 60return 0;
}
使用仿函数的标准库算法
许多标准库算法可以接受仿函数作为参数,使得算法的行为更加灵活和可定制。
#include <iostream>
#include <vector>
#include <algorithm>// 仿函数:判断一个数是否大于某个阈值
struct IsGreaterThan {int threshold;IsGreaterThan(int t) : threshold(t) {}bool operator()(int x) const {return x > threshold;}
};int main() {std::vector<int> numbers = {1, 5, 10, 15, 20};// 使用仿函数进行筛选IsGreaterThan greaterThan10(10);auto it = std::find_if(numbers.begin(), numbers.end(), greaterThan10);if(it != numbers.end()) {std::cout << "第一个大于10的数是: " << *it << std::endl; // 输出: 第一个大于10的数是: 15} else {std::cout << "没有找到大于10的数。" << std::endl;}return 0;
}
仿函数与模板
仿函数与模板相结合,可以实现高度通用和可复用的代码。例如,编写一个通用的比较仿函数。
#include <iostream>
#include <vector>
#include <algorithm>// 通用比较仿函数
template <typename T>
struct Compare {bool operator()(const T& a, const T& b) const {return a < b;}
};int main() {std::vector<int> numbers = {5, 2, 8, 1, 9};// 使用仿函数进行排序std::sort(numbers.begin(), numbers.end(), Compare<int>());std::cout << "排序后的数字: ";for(auto num : numbers) {std::cout << num << " "; // 输出: 1 2 5 8 9}std::cout << std::endl;return 0;
}
额外知识
- 仿函数的类型
#include <type_traits>
#include <iostream>struct F {int operator()(int) const {return 0;}
};int main() {F f;static_assert(!std::is_function<decltype(f)>::value); // f不是函数static_assert(std::is_class<decltype(f)>::value); // f是类static_assert(std::is_same<decltype(f), F>::value); // f的类型是Fstd::cout << f(10) << std::endl;
}
仿函数的优势
- 可扩展性: 能够根据需要添加更多功能和状态。
- 与Lambda的互补性: 在需要携带复杂状态或多次调用时,仿函数比Lambda更适合。
- 类型安全: 仿函数是具体的类型,可以在编译期进行类型检查。
何时使用仿函数
- 需要携带状态时: 当回调函数需要维护内部状态时,仿函数是理想选择。
- 复杂操作: 当简单的函数指针或Lambda难以表达复杂逻辑时。
- 性能关键场景: 由于仿函数可以被编译器优化,适用于性能敏感的代码。