当前位置:   article > 正文

【C++ 11】(二)基于范围的for循环_c++基于范围的for循环

c++基于范围的for循环

1. 概要说明

基于范围的for循环(Range-based for loop)是C++11引入的一种新的循环结构,基于范围的for循环可以遍历支持迭代器的集合,如std::vector、std::list等,也可以遍历支持下标操作的集合,如std::array和普通数组。掌握基于范围的for循环,它可以方便地操作集合中的元素,而无需手动管理索引或迭代器,将有助于你编写出更高效、更优雅的C++代码。

2 如何使用基于范围的for循环

2.1 语法格式

基于范围的for循环的语法形式如下:

  1. for (for-range-declaration : for-range-initializer) {
  2. // 循环体
  3. }

在这里,for-range-declaration是一个迭代变量,用于表示每一次迭代中从for-range-initializer中获取的元素的值或引用,for-range-initializer是一个表示容器或数组的表达式。迭代变量可以直接声明成for-range-initializer中元素的类型,或者声明成auto类型,使代码更加简洁。

2.2 迭代变量

2.2.1 基础类型

一般情况下,范围for循环内的迭代变量的类型应该声明成被遍历的数据、容器集合中的数据元素的类型。如下:

  1. int arr[] = {1, 2, 3, 4, 5};
  2. for (int i : arr) {
  3. std::cout << i << " ";
  4. }
  5. std::vector<float> vec = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};
  6. for (float i : vec) {
  7. std::cout << i << " ";
  8. }

2.2.2 自动类型推断

范围fo循环可以和C++11的别一个特性:自动类型推断(auto)一起使用,使代码更加简洁。一般情况下,迭代变量可声明成auto、 auto &(左值引用类型)、auto &&(右值引用类型)、const auto &(常量引用类型)。

2.2.2.1 auto 类型

使用auto类型的迭代变量进行遍历集合时,实际上迭代变量只是集合中数据元素的拷贝。在这种情况下,循环体中不论怎么修改迭代变量的数据,都不会影响到原有集合中的数据元素。示例如下:

  1. vector<int> v{1,2,3,4,5};
  2. for(auto i : v) {
  3. i++;
  4. cout << i << " "; // 输出2 3 4 5 6
  5. }
  6. for(vector<int>::iterator it = v.begin(); it != v.end(); it++){
  7. cout << *it << " "; // 输出1 2 3 4 5
  8. }

2.2.2.2 auto &类型

使用auto &类型的迭代变量进行遍历时,迭代变量是一个非常量左值引用,它是集合中数据元素的引用,如果对迭代变量进行修改,会直接影响到集合数据元素本身。示例如下:

  1. vector<int> v{1,2,3,4,5};
  2. for(auto& i : v) {
  3. i++;
  4. cout << i << " "; // 输出2 3 4 5 6
  5. }
  6. for(vector<int>::iterator it = v.begin(); it != v.end(); it++){
  7. cout << *it << " "; // 输出2 3 4 5 6
  8. }

2.2.2.3 auto &&类型 

若for-range-initializer集合返回一个临时对象(将死值),由于临时对象不能绑定在非常量左值引用,因此使用auto &声明迭代变量时会发生编译错误。这个时就需要使用 auto && 非常量右值引用声明迭代变量。示例如下:

  1. vector<bool> vec {true, false, true, false};
  2. // for (auto &a : vec) { // 编译报错
  3. for (auto &&a : vec) { // 编译通过
  4. cout << a << " ";
  5. }

输出:

1 0 1 0

这个时候,我们会提出疑问为什么vetor<bool> 类型的集合,只能使用auto &&类型的迭代变量访问而不能使用auto &类型的迭代变量访问呢?这是因为vector<bool>集合中的元素vector<bool>::reference类型,它不是一个左值,这也是使用auto &时会编译出错的原因。

  1. vector<bool> vec {true, false, true, false};
  2. bool &v = vec[0]; // 编译报错, vec[0]不是一个左值
  3. bool *w = &vec; // 编译报错,无法将一个临时变量地址绑定给指针
  4. auto &&u = vec[0]; // 编译通过,非常量右值引用

关于右值引用的详细说明,请参考【C++ 11】(八)右值引用

2.2.2.4 const auto&类型

在auto &的前面加上了const修饰,意味着迭代变量是集合中数据元素的引用,但是它是只读的不能被修改。示例如下:

  1. vector<int> v{1,2,3,4,5};
  2. for(const auto& i : v) {
  3. i++; // 编译出错
  4. cout << i << " ";
  5. }

 

3.使用注意 

2.1 循环中修改集合中元素个数

范围for循环内进行遍历操作时,如果修改了集合中元素的个数,那么迭代器将会无效。

如下例子中,在集合追加一个元素后,迭代器会无效,输出结果自然就会是错误的。

  1. #include <vector>
  2. #include <iostream>
  3. int main(void) {
  4. std::vector<int> v{ 5, 5, 0, 5, 1 };
  5. for(auto&& i : v) {
  6. std::cout << ' ' << i;
  7. if (5 == i) {
  8. v.emplace_back(123); // 追加元素后、迭代器无效
  9. }
  10. }
  11. }

 结果:

5 5 -1978514432 -946396368 1

2.2 迭代器的生命周期结束无效

迭代器(对象)的生命周期结束后,此时遍历这个对象,也会输出不正确结果甚至发生崩溃现象。

例子如下:

  1. #include <initializer_list>
  2. #include <iostream>
  3. #include <vector>
  4. struct something
  5. {
  6. std::vector<int> v;
  7. something(const std::initializer_list<int>& l ) : v(l) {}
  8. std::vector<int>& get_vector() { return v; }
  9. ~something() noexcept { std::cout << "destructor" << std::endl; }
  10. };
  11. int main()
  12. {
  13. for( auto e : something { 1,2,3,4,5,6,7,8,9,0 }.get_vector() )
  14. {
  15. std::cout << e; // something是临时对象、此时它的的生命周期已结束
  16. }
  17. std::cout << std::endl;
  18. }

4. 结束语

范围for循环是C++11引入的一个强大的新特性,它大大简化了遍历数组和容器的代码,使代码更加简洁,更具可读性。掌握范围for循环,将有助于你编写出更高效、更优雅的C++代码。

 

本文内容由网友自发贡献,转载请注明出处:【wpsshop博客】
推荐阅读
相关标签
  

闽ICP备14008679号