基于范围的for循环

C++11 为我们带来了许多极大提升代码可读性与开发效率的新特性,其中之一就是 基于范围的 for 循环(range-based for loop)。它让我们在遍历容器(如数组、std::vector、std::map 等)时,不再需要写复杂的下标或迭代器,大大简化了代码。

1. 为什么需要基于范围的for循环?

在 C++11 之前,遍历一个容器的常用方法是这样:

1
2
3
4
std::vector<int> nums = {1, 2, 3, 4, 5};
for (std::vector<int>::iterator it = nums.begin(); it != nums.end(); ++it) {
std::cout << *it << " ";
}

这种写法虽然功能完整,但冗长且不利于阅读。
而 C++11 引入的 基于范围的 for 循环,让代码变得更简单:

1
2
3
for (auto n : nums) {
std::cout << n << " ";
}

2.基本语法

1
2
3
for (declaration : container) {
// 循环体
}
  • declaration:用于接收容器中的每个元素(可以是值、引用、常量引用等)。
  • container:任何支持 begin() 和 end() 的容器,例如数组、std::vector、std::list 等。

示例1:简单遍历std::vector

1
2
3
4
5
6
7
8
9
10
11
#include <iostream>
#include <vector>

int main() {
std::vector<int> nums = {1, 2, 3, 4, 5};

for (auto n : nums) {
std::cout << n << " ";
}
return 0;
}

输出:

1
1 2 3 4 5

示例 2:修改容器内容(使用引用)

默认情况下,auto n 是按值复制的,修改 n 不会影响原容器。如果你需要修改容器中的内容,应使用引用:

1
2
3
for (auto& n : nums) {
n *= 2;
}

或者防止被修改时使用常量引用:

1
2
3
for (const auto& n : nums) {
std::cout << n << " ";
}

示例 3:遍历原生数组

1
2
3
4
int arr[] = {1, 2, 3, 4, 5};
for (auto n : arr) {
std::cout << n << " ";
}

示例4:访问次数

基于范围的for循环遍历的对象可以是一个表达式或者容器/数组等。假设我们对一个容器进行遍历,在遍历过程中for循环对这个容器的访问频率是一次还是多次呢?我们通过下面的例子验证一下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <iostream>
#include <vector>
using namespace std;

vector<int> v{ 1,2,3,4,5,6 };
vector<int>& getRange()
{
cout << "get vector range..." << endl;
return v;
}

int main(void)
{
for (auto val : getRange())
{
cout << val << " ";
}
cout << endl;

return 0;
}

输出的结果如下:

1
2
get vector range...
1 2 3 4 5 6

从上面的结果中可以看到,不论基于范围的for循环迭代了多少次,函数getRange()只在第一次迭代之前被调用,得到这个容器对象之后就不会再去重新获取这个对象了。