一、for_each概述
for_each
常用于遍历容器并对每个元素应用特定的函数或函数对象。
头文件
要使用 for_each
,需要包含头文件:
#include <algorithm>
二、for_each 的语法
1.基础语法
for_each
有以下基本语法:
template <class InputIterator, class Function>
Function for_each(InputIterator first, InputIterator last, Function f);
-
模板参数:
InputIterator
:输入迭代器类型,表示要遍历的范围。Function
:可调用对象类型,用于对每个元素执行操作。
-
参数说明:
first
,last
:输入范围的起始和结束迭代器,遍历范围为[first, last)
。f
:函数或函数对象,对每个元素执行的操作。
-
返回值:
- 返回传入的函数对象
f
,其可能被修改过(例如,包含了累积的状态)。
- 返回传入的函数对象
2.参数详解
输入迭代器(InputIterator)
for_each
支持各种类型的迭代器,包括指针、vector
的迭代器、list
的迭代器等。确保输入范围有效且迭代器类型满足要求。
可调用对象(Function)
可调用对象可以是以下几种形式:
- 函数指针:指向一个符合要求的普通函数。
- 函数对象(Functor):重载了
operator()
的类或结构体。 - Lambda 表达式:C++11 引入的匿名函数,适用于简洁的操作定义。
三、使用示例
示例 1:使用函数指针
需求:遍历一个整数向量,打印每个元素。
#include <iostream>
#include <vector>
#include <algorithm>
// 定义一个普通函数
void printElement(int x) {
std::cout << x << " ";
}
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用函数指针作为操作
std::for_each(numbers.begin(), numbers.end(), printElement);
std::cout << std::endl; // 输出:1 2 3 4 5
return 0;
}
解释:
printElement
函数被传递给std::for_each
,对每个元素执行打印操作。
执行结果
1 2 3 4 5
示例 2:使用 Lambda 表达式
需求:遍历一个字符串向量,打印每个字符串。
#include <iostream>
#include <vector>
#include <algorithm>
#include <string>
int main() {
std::vector<std::string> words = {"Hello", "World", "C++", "for_each"};
// 使用 Lambda 表达式作为操作
std::for_each(words.begin(), words.end(), [](const std::string& word) {
std::cout << word << " ";
});
std::cout << std::endl; // 输出:Hello World C++ for_each
return 0;
}
解释:
- Lambda 表达式提供了更简洁的语法,直接在
std::for_each
中定义操作逻辑。
执行结果
Hello World C++ for_each
示例 3:使用函数对象(Functor)
需求:计算一个整数向量的总和。
#include <iostream>
#include <vector>
#include <algorithm>
// 定义一个函数对象
struct Sum {
int total;
Sum() : total(0) {}
void operator()(int x) {
total += x;
}
};
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
Sum sum;
// 使用引用传递函数对象
std::for_each(numbers.begin(), numbers.end(), [&](int x) {
sum(x);
});
// 输出总和
std::cout << "总和: " << sum.total << std::endl; // 输出:总和: 15
return 0;
}
执行结果
15
示例 4:修改容器中的元素
需求:将一个整数向量中的每个元素加1。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
// 使用 Lambda 表达式修改每个元素
std::for_each(numbers.begin(), numbers.end(), [](int& x) {
x += 1;
});
// 输出修改后的向量
for(const auto& num : numbers) {
std::cout << num << " "; // 输出:2 3 4 5 6
}
std::cout << std::endl;
return 0;
}
解释:
- Lambda 表达式接受元素的引用,直接修改原有元素的值。
执行结果
2 3 4 5 6
五、注意事项
1. 可调用对象的副作用
std::for_each
主要用于执行有副作用的操作(如修改外部变量、打印等),而不返回结果。确保操作的设计符合预期,避免不必要的副作用。
2. 函数对象的复制
std::for_each
通常会对可调用对象进行拷贝。因此,如果可调用对象内部有状态变化(如累加总和),需要确保在外部访问时获取正确的状态(如使用引用或在操作中进行状态更新)。
3. 修改容器元素
如果需要修改容器中的元素,确保操作函数接受元素的引用。例如:
std::for_each(container.begin(), container.end(), [](ElementType& x) {
// 修改 x
});
否则,只会修改元素的副本,不影响原有容器。
4. 性能考虑
- 避免不必要的拷贝:对于大型对象,尽量使用引用或指针传递,减少拷贝开销。
- 可并行执行:虽然
std::for_each
本身不支持并行执行,但可以结合多线程技术实现并行处理,提升性能。
六、进阶用法
1. 使用 Lambda 表达式捕获外部变量
需求:统计一个整数向量中大于某个阈值的元素数量。
#include <iostream>
#include <vector>
#include <algorithm>
int main() {
std::vector<int> numbers = {5, 10, 15, 20, 25};
int threshold = 12;
int count = 0;
// 使用 Lambda 表达式捕获 threshold 和 count
std::for_each(numbers.begin(), numbers.end(), [&](int x) {
if(x > threshold) {
count++;
}
});
std::cout << "大于 " << threshold << " 的元素数量: " << count << std::endl; // 输出:大于 12 的元素数量: 3
return 0;
}
解释:
- Lambda 表达式通过引用捕获
threshold
和count
,在操作中更新count
。
执行结果
大于 12 的元素数量: 3
2. 结合 for_each
和异常处理
需求:在遍历容器时,如果遇到特定条件的元素,抛出异常并捕获处理。
#include <iostream>
#include <vector>
#include <algorithm>
#include <stdexcept>
int main() {
std::vector<int> numbers = {1, 2, 3, 4, 5};
try {
std::for_each(numbers.begin(), numbers.end(), [](int x) {
if(x == 3) {
throw std::runtime_error("遇到元素3,抛出异常");
}
std::cout << x << " ";
});
} catch(const std::exception& e) {
std::cerr << "\n异常捕获: " << e.what() << std::endl; // 输出:异常捕获: 遇到元素3,抛出异常
}
return 0;
}
解释:
- 当遍历到元素
3
时,抛出异常并在外部捕获。
执行结果
1 2
异常捕获: 遇到元素3,抛出异常
3. 使用 for_each 修改外部数据结构
需求:根据一个整数向量,构建一个频率统计的哈希表。
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
int main() {
std::vector<int> numbers = {1, 2, 2, 3, 3, 3, 4, 4, 4, 4};
std::unordered_map<int, int> frequency;
// 使用 Lambda 表达式更新频率表
std::for_each(numbers.begin(), numbers.end(), [&](int x) {
frequency[x]++;
});
// 输出频率表
for(const auto& pair : frequency) {
std::cout << "元素 " << pair.first << " 出现 " << pair.second << " 次。" << std::endl;
}
// 输出:
// 元素 1 出现 1 次。
// 元素 2 出现 2 次。
// 元素 3 出现 3 次。
// 元素 4 出现 4 次。
return 0;
}
解释:
- Lambda 表达式捕获
frequency
哈希表,通过递增计数器统计每个元素的出现次数。
执行结果
元素 4 出现 4 次。
元素 3 出现 3 次。
元素 2 出现 2 次。
元素 1 出现 1 次。