C++ 预定义函数对象
什么是预定义函数对象?
在C++的STL(标准模板库)中,预定义函数对象是指由STL预先定义好的一组函数对象(functors),用于执行各种常见操作,如比较、数学运算和逻辑判断等。它们实际上是重载了函数调用运算符operator()
的类,使其实例可以像函数一样被调用。
这些预定义函数对象主要位于<functional>
头文件中,为我们提供了一系列可直接使用的功能,无需自己编写相应的函数或函数对象。
提示
预定义函数对象比普通函数更高效,因为它们可以被内联展开,减少函数调用开销。
预定义函数对象的分类
C++预定义函数对象可以分为三大类:
- 算术函数对象 - 用于执行基本的算术运算
- 关系函数对象 - 用于比较两个值
- 逻辑函数对象 - 用于执行逻辑运算
下面我们将详细介绍每种类型。
1. 算术函数对象
算术函数对象用于执行基本的算术运算,包括:
函数对象 | 描述 | 等价表达式 |
---|---|---|
plus<T> | 加法 | x + y |
minus<T> | 减法 | x - y |
multiplies<T> | 乘法 | x * y |
divides<T> | 除法 | x / y |
modulus<T> | 取模(求余) | x % y |
negate<T> | 取反 | -x |
让我们看一个简单的例子,展示如何使用这些算术函数对象:
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
int sum = 0;
// 使用plus<int>累加vector中的所有元素
std::plus<int> adder;
for (int num : numbers) {
sum = adder(sum, num);
}
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15
// 使用multiplies<int>计算vector中所有元素的乘积
int product = 1;
std::multiplies<int> multiplier;
for (int num : numbers) {
product = multiplier(product, num);
}
std::cout << "Product: " << product << std::endl; // 输出: Product: 120
// 使用negate<int>将所有元素取反
std::vector<int> negated;
std::negate<int> negator;
for (int num : numbers) {
negated.push_back(negator(num));
}
std::cout << "Negated: ";
for (int num : negated) {
std::cout << num << " "; // 输出: Negated: -1 -2 -3 -4 -5
}
std::cout << std::endl;
return 0;
}
2. 关系函数对象
关系函数对象用于比较两个值,返回一个布尔值:
函数对象 | 描述 | 等价表达式 |
---|---|---|
equal_to<T> | 等于 | x == y |
not_equal_to<T> | 不等于 | x != y |
greater<T> | 大于 | x > y |
less<T> | 小于 | x < y |
greater_equal<T> | 大于等于 | x >= y |
less_equal<T> | 小于等于 | x <= y |
以下是使用关系函数对象的示例:
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9};
// 使用greater<int>降序排序
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
std::cout << "Descending order: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: Descending order: 9 8 5 2 1
}
std::cout << std::endl;
// 使用less<int>升序排序(这是默认的排序方式)
std::sort(numbers.begin(), numbers.end(), std::less<int>());
std::cout << "Ascending order: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: Ascending order: 1 2 5 8 9
}
std::cout << std::endl;
// 使用equal_to<int>查找特定元素
std::equal_to<int> equals_five;
int count = 0;
for (int num : numbers) {
if (equals_five(num, 5)) {
count++;
}
}
std::cout << "Number of elements equal to 5: " << count << std::endl; // 输出: Number of elements equal to 5: 1
return 0;
}
3. 逻辑函数对象
逻辑函数对象用于执行逻辑运算:
函数对象 | 描述 | 等价表达式 |
---|---|---|
logical_and<T> | 逻辑与 | x && y |
logical_or<T> | 逻辑或 | x || y |
logical_not<T> | 逻辑非 | !x |
下面是逻辑函数对象的使用示例:
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<bool> values = {true, false, true, false};
// 使用logical_not<bool>取反所有元素
std::vector<bool> negated;
std::transform(values.begin(), values.end(),
std::back_inserter(negated),
std::logical_not<bool>());
std::cout << "Original: ";
for (bool val : values) {
std::cout << (val ? "true " : "false "); // 输出: Original: true false true false
}
std::cout << std::endl;
std::cout << "Negated: ";
for (bool val : negated) {
std::cout << (val ? "true " : "false "); // 输出: Negated: false true false true
}
std::cout << std::endl;
// 使用logical_and<bool>检查是否所有元素都为true
bool all_true = true;
std::logical_and<bool> logical_and;
for (bool val : values) {
all_true = logical_and(all_true, val);
}
std::cout << "All true? " << (all_true ? "Yes" : "No") << std::endl; // 输出: All true? No
// 使用logical_or<bool>检查是否至少有一个元素为true
bool any_true = false;
std::logical_or<bool> logical_or;
for (bool val : values) {
any_true = logical_or(any_true, val);
}
std::cout << "Any true? " << (any_true ? "Yes" : "No") << std::endl; // 输出: Any true? Yes
return 0;
}
在STL算法中应用预定义函数对象
预定义函数对象最常见的应用场景是在STL算法中作为谓词(predicate)或比较器(comparator)。
排序与查找
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 2, 8, 1, 9, 3, 7, 4, 6};
// 使用greater<int>进行降序排序
std::sort(numbers.begin(), numbers.end(), std::greater<int>());
std::cout << "Sorted (descending): ";
for (int num : numbers) {
std::cout << num << " "; // 输出: 9 8 7 6 5 4 3 2 1
}
std::cout << std::endl;
// 查找第一个大于5的元素
auto it = std::find_if(numbers.begin(), numbers.end(),
std::bind(std::greater<int>(), std::placeholders::_1, 5));
if (it != numbers.end()) {
std::cout << "First element greater than 5: " << *it << std::endl; // 输出: First element greater than 5: 9
}
return 0;
}
统计与筛选
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
// 计算大于5的元素数量
int count = std::count_if(numbers.begin(), numbers.end(),
std::bind(std::greater<int>(), std::placeholders::_1, 5));
std::cout << "Number of elements greater than 5: " << count << std::endl; // 输出: Number of elements greater than 5: 5
// 移除所有小于5的元素
numbers.erase(
std::remove_if(numbers.begin(), numbers.end(),
std::bind(std::less<int>(), std::placeholders::_1, 5)),
numbers.end()
);
std::cout << "After removing elements less than 5: ";
for (int num : numbers) {
std::cout << num << " "; // 输出: After removing elements less than 5: 5 6 7 8 9 10
}
std::cout << std::endl;
return 0;
}
实际应用案例
案例1:自定义数据结构排序
假设我们有一个表示学生的结构体,需要根据成绩对学生进行排序:
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <string>
// 学生结构体
struct Student {
std::string name;
int score;
Student(const std::string& n, int s) : name(n), score(s) {}
};
int main() {
std::vector<Student> students = {
{"Alice", 85},
{"Bob", 92},
{"Charlie", 78},
{"David", 90},
{"Eve", 95}
};
// 使用greater<int>按分数降序排序(高分在前)
std::sort(students.begin(), students.end(),
[](const Student& a, const Student& b) {
return std::greater<int>()(a.score, b.score);
});
std::cout << "Students sorted by score (high to low):" << std::endl;
for (const auto& student : students) {
std::cout << student.name << ": " << student.score << std::endl;
}
return 0;
}
输出结果:
Students sorted by score (high to low):
Eve: 95
Bob: 92
David: 90
Alice: 85
Charlie: 78
案例2:自定义优先队列
使用预定义函数对象创建一个最小堆的优先队列:
cpp
#include <iostream>
#include <functional>
#include <queue>
#include <vector>
int main() {
// 默认的优先队列是最大堆
std::priority_queue<int> max_heap;
// 使用less<int>创建一个最小堆
std::priority_queue<int, std::vector<int>, std::greater<int>> min_heap;
// 添加相同的元素到两个堆
for (int num : {3, 1, 4, 1, 5, 9, 2, 6}) {
max_heap.push(num);
min_heap.push(num);
}
std::cout << "Max heap elements (popped in order): ";
while (!max_heap.empty()) {
std::cout << max_heap.top() << " "; // 输出: 9 6 5 4 3 2 1 1
max_heap.pop();
}
std::cout << std::endl;
std::cout << "Min heap elements (popped in order): ";
while (!min_heap.empty()) {
std::cout << min_heap.top() << " "; // 输出: 1 1 2 3 4 5 6 9
min_heap.pop();
}
std::cout << std::endl;
return 0;
}
案例3:函数式编程风格的数据处理
使用预定义函数对象实现函数式编程风格的数据处理:
cpp
#include <iostream>
#include <functional>
#include <vector>
#include <algorithm>
#include <numeric>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用plus<int>和accumulate计算总和
int sum = std::accumulate(numbers.begin(), numbers.end(), 0, std::plus<int>());
std::cout << "Sum: " << sum << std::endl; // 输出: Sum: 15
// 使用multiplies<int>和accumulate计算乘积
int product = std::accumulate(numbers.begin(), numbers.end(), 1, std::multiplies<int>());
std::cout << "Product: " << product << std::endl; // 输出: Product: 120
// 使用transform和negate<int>创建一个新的容器,其中包含所有元素的相反数
std::vector<int> negated(numbers.size());
std::transform(numbers.begin(), numbers.end(), negated.begin(), std::negate<int>());
std::cout << "Negated: ";
for (int num : negated) {
std::cout << num << " "; // 输出: Negated: -1 -2 -3 -4 -5
}
std::cout << std::endl;
return 0;
}
C++ 11的无模板参数函数对象
从C++11开始,STL允许我们在使用预定义函数对象时省略模板参数,编译器可以根据上下文推导出类型:
cpp
// C++11之前
std::sort(v.begin(), v.end(), std::greater<int>());
// C++11及以后
std::sort(v.begin(), v.end(), std::greater<>()); // 编译器自动推导类型
这使代码更加简洁,特别是在处理复杂类型时。
总结
C++预定义函数对象是STL提供的强大工具,能让我们以简洁的方式执行常见操作:
- 算术函数对象 (
plus<>
,minus<>
,multiplies<>
等) 用于执行基本数学运算 - 关系函数对象 (
greater<>
,less<>
,equal_to<>
等) 用于比较值 - 逻辑函数对象 (
logical_and<>
,logical_or<>
,logical_not<>
) 用于逻辑操作
这些函数对象的主要优势包括:
- 高效性 - 它们可以被内联展开,减少函数调用开销
- 可复用性 - 可以在不同的STL算法中重复使用
- 类型安全 - 编译时类型检查确保操作的安全性
- 灵活性 - 可以作为参数传递给其他函数或算法
预定义函数对象在STL算法、容器排序和数据处理中特别有用,是现代C++编程不可或缺的部分。
练习
- 使用
std::transform
和std::multiplies<>
将一个整数向量中的所有元素乘以2。 - 创建一个程序,使用
std::sort
和std::greater<>
按照字符串长度降序排序一个字符串向量。 - 实现一个函数,使用
std::count_if
和适当的函数对象,计算一个整数向量中偶数的数量。 - 使用
std::accumulate
和std::plus<>
计算一个包含浮点数的向量的平均值。 - 创建一个自定义结构体,并使用预定义函数对象作为比较器,实现按照多个字段排序。
进一步学习资源
- C++ Reference - Functional
- C++标准库
<functional>
头文件 - 阅读《Effective STL》 by Scott Meyers,特别是关于函数对象的章节
- 《C++ STL标准库》by Nicolai Josuttis,深入了解函数对象和算法
通过充分理解和掌握预定义函数对象,你将能够编写更加简洁、高效和可维护的C++代码。