C++11新特性——for遍历

熟悉C++98/03的对于for循环就再了解不过了,如果我们要遍历一个数组,那么在C++98/03中的实现方式:

  1. int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  2. for (int i = 0; i < 10; i++)
  3. cout << arr[i];

而遍历容器类的For如下:
  1. std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
  2. for (std::vector<int>::iterator itr = vec.begin(); itr != vec.end(); itr++)
  3. cout << *itr;

不管上面哪一种方法,都必须明确的确定for循环开头以及结尾条件,而熟悉C#或者python的人都知道在C#和python中存在一种for的使用方法不需要明确给出容器的开始和结束条件,就可以遍历整个容器,幸运的是C++11中引入了这种方法也就是基于范围的for循环,用基于范围的for循环改写上面两个例子:
  1. int arr[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  2. for (auto n : arr)
  3. cout << n;
  4. std::vector<int> vec {1,2,3,4,5,6,7,8,9,10};
  5. for (auto n :vec)
  6. std::cout << n;

可以看到改写后的使用方法简单了很多,代码的可读性提升了一个档次,但是需要注意的在上述对容器的遍历是只读的,也就是说遍历的值是不可修改的,如果需要修改其中元素,可以声明为auto &:

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. int main()
  5. {
  6. std::vector<int> vec{ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  7. cout << "修改前" << endl;
  8. for (auto &n : vec)
  9. std::cout << n++;
  10. cout << endl;
  11. cout << "修改后" << endl;
  12. for (auto j : vec)
  13. std::cout << j;
  14. cout << endl;
  15. system("pause");
  16. return 0;
  17. }

使用时需要注意的地方

1.注意auto自动推导的类型

虽然基于范围的for循环使用起来非常的方便,我们不用再去关注for的开始条件和结束条件等问题了,但是还是有一些细节问题在使用的时候需要注意,来看下对于容器map的遍历:

  1. std::map<string, int> map = { { "a", 1 }, { "b", 2 }, { "c", 3 } };
  2. for (auto &val : map)
  3. cout << val.first << "->" << val.second << endl;
为什么是使用val.first,val.second而不是直接输出value呢?在遍历容器的时候,auto自动推导的类型是容器的value_type类型,而不是迭代器,而map中的value_type是std::pair,也就是说val的类型是std::pair类型的,因此需要使用val.first,val.second来访问数据。

2.注意容器本身的约束

使用基于范围的for循环还要注意一些容器类本身的约束,比如set的容器内的元素本身有容器的特性就决定了其元素是只读的,哪怕的使用了引用类型来遍历set元素,也是不能修改器元素的,看下面例子:

  1. set<int> ss = { 1, 2, 3, 4, 5, 6 };
  2. for (auto& n : ss)
  3. cout << n++ << endl;
上述代码定义了一个set,使用引用类型遍历set中的元素,然后对元素的值进行修改,该段代码编译失败:error C3892: 'n' : you cannot assign to a variable that is const。同样对于map中的first元素也是不能进行修改的。

3.当冒号后不是容器而是一个函数

再来看看假如我们给基于范围的for循环的:冒号后面的表达式不是一个容器而是一个函数,看看函数会被调用多少次?

  1. #include <iostream>
  2. #include <set>
  3. using namespace std;
  4. set<int> ss = { 1, 2, 3, 4, 5, 6 };
  5. const set<int> getSet()
  6. {
  7. cout << "GetSet" << endl;
  8. return ss;
  9. }
  10. int main()
  11. {
  12. for (auto n : getSet())
  13. cout << n << endl;
  14. system("pause");
  15. return 0;
  16. }


可以看出,如果冒号后面的表达式是一个函数调用时,函数仅会被调用一次。

4.不要在for循环中修改容器

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. vector<int> vec = { 1, 2, 3, 4, 5, 6 };
  5. int main()
  6. {
  7. for (auto n : vec)
  8. {
  9. cout << n << endl;
  10. vec.push_back(7);
  11. }
  12. system("pause");
  13. return 0;
  14. }
上述代码在遍历vector时,在容器内插入一个元素7,运行上述代码程序崩溃了。

究其原因还是由于在遍历容器的时候,在容器中插入一个元素导致迭代器失效了,因此,基于范围的for循环和普通的for循环一样,在遍历的过程中如果修改容器,会造成迭代器失效,(有关迭代器失效的问题请参阅C++ primer这本书,写的很详细)也就是说基于范围的for循环的内部实现机制还是依赖于迭代器的相关实现。


参考链接:http://blog.csdn.net/hailong0715/article/details/54172848

posted @ 2019-05-19 21:25  unique_ptr  阅读(16163)  评论(1编辑  收藏  举报