仿函数
在 C++ 中,仿函数(Function Object 或 Functor) 是一种行为类似于函数的对象。仿函数是通过重载 operator()
运算符来实现的,这样一个对象就可以像函数一样调用。
仿函数的核心思想是:对象可以具有类似函数的行为,并且可以携带状态。这为设计灵活的函数调用方式提供了可能性,尤其是在 STL(标准模板库)中,仿函数被广泛应用于算法、容器操作等场景。
1. 仿函数的基本概念
仿函数是通过在一个类中重载 operator()
运算符来实现的。这样,类的对象就可以被调用(表现得像一个函数)。
仿函数的基本语法
1 |
|
输出
1 | Called with: 10 |
在这个例子中,functor(10)
的调用会自动调用 MyFunctor
类的 operator()
函数。这样,functor
对象就表现得像一个函数。
2. 仿函数的优点
携带状态:
仿函数可以包含成员变量,用于保存状态,而普通函数无法做到这一点。灵活性和可扩展性:
仿函数是一个类,可以拥有额外的功能,如构造函数、成员函数等,提供比普通函数更多的功能和灵活性。STL 算法支持:
仿函数与 STL 算法(如std::sort
、std::for_each
)配合得非常好,用于定义自定义的行为。
3. 仿函数的应用
3.1 仿函数与 STL 算法结合
仿函数可以作为参数传递给 STL 算法,例如 std::sort
。
1 |
|
输出
1 | 8 5 3 2 1 |
说明:
- 仿函数
Compare
定义了一个自定义排序规则:返回true
时表示a
应该排在b
的前面。 std::sort
接受仿函数作为第三个参数,用于对容器中的元素进行排序。
3.2 状态保存的仿函数
仿函数可以保存状态,普通函数无法做到这一点。
1 |
|
输出
1 | 8 |
说明:
- 仿函数
Adder
内部保存了一个状态value
,通过构造函数进行初始化。 - 调用仿函数时,可以使用该状态进行计算,类似于闭包(closure)的效果。
3.3 使用 STL 的 std::for_each
仿函数可以用于 STL 算法 std::for_each
,实现对容器中每个元素的操作。
1 |
|
输出
1 | 1 2 3 4 5 |
说明:
Print
仿函数定义了如何处理每个元素。std::for_each
将容器中的每个元素依次传递给仿函数。
3.4 Lambda 表达式的替代
在现代 C++(C++11 及以上)中,仿函数可以被 Lambda 表达式 替代。Lambda 表达式更简洁,但仿函数在某些复杂场景下依然有优势。
等价于上面的 std::for_each
示例:
1 |
|
输出
1 | 1 2 3 4 5 |
4. STL 中常见的仿函数
C++ STL 提供了一些常用的内置仿函数,主要定义在头文件 <functional>
中:
4.1 算术仿函数
std::plus
:加法。std::minus
:减法。std::multiplies
:乘法。std::divides
:除法。std::modulus
:取模。std::negate
:取负。
1 |
|
4.2 逻辑仿函数
std::equal_to
:等于。std::not_equal_to
:不等于。std::greater
:大于。std::less
:小于。std::greater_equal
:大于等于。std::less_equal
:小于等于。
4.3 逻辑操作仿函数
std::logical_and
:逻辑与。std::logical_or
:逻辑或。std::logical_not
:逻辑非。
5. 仿函数与 Lambda 的对比
特性 | 仿函数 | Lambda 表达式 |
---|---|---|
状态管理 | 通过类的成员变量保存状态 | 捕获变量(自动或手动捕获) |
代码长度 | 需要定义一个类,代码较长 | 简洁(C++11 及以上) |
扩展性 | 灵活,可定义复杂行为 | 简单场景更适用 |
兼容性 | C++98 开始支持 | C++11 及以上支持 |
总结
- 仿函数是通过重载
operator()
实现的对象,可以像函数一样调用。 - 仿函数能够保存状态,并灵活应用于 STL 算法。
- 在现代 C++ 中,仿函数仍然有其独特的作用,尤其是在复杂的场景中,而简单场景更推荐使用 Lambda 表达式。
All articles on this blog are licensed under CC BY-NC-SA 4.0 unless otherwise stated.
Comments