函数对象是重载了operator()的类实例,可像函数一样调用,能携带状态且与标准库算法配合实现行为参数化,相比函数指针更灵活安全,适用于需要状态保持或泛型编程的场景。

C++的函数对象,说白了,就是那些重载了
operator()
要实现一个函数对象,核心就是定义一个类,并在其中重载圆括号运算符
operator()
举个最简单的例子,我们来创建一个能判断一个数字是否是偶数的函数对象:
#include <iostream>
#include <vector>
#include <algorithm> // for std::for_each, std::sort
// 1. 定义一个类
class IsEven {
public:
// 2. 重载 operator()
bool operator()(int num) const {
return num % 2 == 0;
}
};
// 另一个例子:一个可以累加的函数对象,带有内部状态
class Accumulator {
private:
long long sum_ = 0; // 内部状态
public:
void operator()(int num) {
sum_ += num;
}
long long get_sum() const {
return sum_;
}
};
int main() {
// 使用 IsEven 函数对象
IsEven checkEven; // 创建一个函数对象实例
if (checkEven(4)) { // 像调用函数一样调用它
std::cout << "4 is even." << std::endl;
}
// 将 IsEven 应用到容器中 (例如,配合标准库算法)
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
std::cout << "Even numbers in vector: ";
for (int n : numbers) {
if (checkEven(n)) { // 再次调用
std::cout << n << " ";
}
}
std::cout << std::endl;
// 使用 Accumulator 函数对象
Accumulator acc;
std::for_each(numbers.begin(), numbers.end(), acc); // std::for_each 会对每个元素调用 acc(num)
std::cout << "Sum of numbers: " << acc.get_sum() << std::endl; // 获取累加结果
return 0;
}这段代码展示了函数对象的基本结构和两种典型用法:一种是无状态的判断逻辑,另一种是带有内部状态的累加器。关键在于
operator()
立即学习“C++免费学习笔记(深入)”;
我个人觉得,函数对象之所以重要,是因为它解决了函数指针的一些固有局限性。最初接触C++,我们学习函数指针,觉得那玩意儿挺酷,能把函数当参数传。但用着用着,你就会发现它有几个痛点:
首先,函数指针无法携带状态。一个普通函数指针指向的函数,它执行的逻辑是固定的,不能在调用之间记住任何信息。比如,你想传递一个比较器给
std::sort
Accumulator
sum_
其次,类型安全和泛型编程的适配性。函数指针在泛型编程,特别是模板中,用起来会有些别扭。它们的类型签名必须完全匹配,这在复杂模板元编程中可能成为障碍。而函数对象,因为它们是类类型,可以被模板参数完美地推导和处理,这使得它们与C++的模板机制配合得天衣无缝。
std::sort
说实话,有时候我觉得函数对象就像是给函数套了一个“壳”,这个壳不仅能让函数“记住”东西,还能让它更好地融入C++的类型系统和面向对象范式。它提供了一种比函数指针更强大、更灵活的封装行为的方式。
C++标准库,尤其是
<algorithm>
我们来举几个经典的例子:
std::sort
struct Person {
std::string name;
int age;
};
// 按照年龄降序排序的函数对象
struct ComparePersonByAgeDesc {
bool operator()(const Person& p1, const Person& p2) const {
return p1.age > p2.age; // 年龄大的排在前面
}
};
int main() {
std::vector<Person> people = {
{"Alice", 30}, {"Bob", 25}, {"Charlie", 35}
};
std::sort(people.begin(), people.end(), ComparePersonByAgeDesc());
std::cout << "Sorted by age (desc):" << std::endl;
for (const auto& p : people) {
std::cout << p.name << " (" << p.age << ")" << std::endl;
}
return 0;
}这里
ComparePersonByAgeDesc
std::sort
operator()
std::for_each
std::for_each
// 一个打印并修改元素的函数对象
class PrintAndIncrement {
private:
std::string prefix_;
public:
explicit PrintAndIncrement(const std::string& prefix) : prefix_(prefix) {}
void operator()(int& num) const { // 注意这里是引用,可以修改
std::cout << prefix_ << num << " -> ";
num++; // 修改元素
std::cout << num << std::endl;
}
};
int main() {
std::vector<int> values = {10, 20, 30};
std::cout << "Original values: ";
for (int v : values) std::cout << v << " ";
std::cout << std::endl;
// 使用带状态的函数对象
std::for_each(values.begin(), values.end(), PrintAndIncrement("Value: "));
std::cout << "Modified values: ";
for (int v : values) std::cout << v << " ";
std::cout << std::endl;
return 0;
}PrintAndIncrement
prefix_
operator()
函数对象在标准库算法中的应用,真正体现了“行为参数化”的思想。它让算法本身保持通用性,而具体的行为则由我们提供的函数对象来定义,大大增强了代码的灵活性和复用性。
这三者在C++中都代表了“可调用对象”,但它们各有侧重和适用场景。在我看来,它们是C++在处理“行为”这个概念上,从传统到现代,一步步演进的结果。
函数对象 (Function Objects / Functors - 显式类定义)
operator()
Lambda 表达式
auto
std::function
std::sort
std::for_each
std::function
std::function
std::function
typename F
std::function
std::function
所以,我的选择策略通常是这样的:
std::function
它们不是互相排斥的,很多时候是互补的。比如,一个Lambda表达式在底层就是编译器生成的一个函数对象。而
std::function
以上就是C++函数对象实现 重载operator()示例的详细内容,更多请关注php中文网其它相关文章!
每个人都需要一台速度更快、更稳定的 PC。随着时间的推移,垃圾文件、旧注册表数据和不必要的后台进程会占用资源并降低性能。幸运的是,许多工具可以让 Windows 保持平稳运行。
Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号