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

本文介绍了C++11中基于范围的for循环,包括其语法、迭代变量的使用(基础类型、自动类型推断,以及const、auto&、auto&&的区别),并提醒了在循环中可能遇到的问题,如修改集合元素和迭代器生命周期的注意事项。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 概要说明

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

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

2.1 语法格式

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

for (for-range-declaration : for-range-initializer) {
    // 循环体
}

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

2.2 迭代变量

2.2.1 基础类型

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

int arr[] = {1, 2, 3, 4, 5};

for (int i : arr) {
    std::cout << i << " ";
}

std::vector<float> vec = {1.0f, 2.0f, 3.0f, 4.0f, 5.0f};

for (float i : vec) {
    std::cout << i << " ";
}

2.2.2 自动类型推断

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

2.2.2.1 auto 类型

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

vector<int> v{1,2,3,4,5};

for(auto i : v) {
    i++;
    cout << i << " "; // 输出2 3 4 5 6
}

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

2.2.2.2 auto &类型

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

vector<int> v{1,2,3,4,5};

for(auto& i : v) {
    i++;
    cout << i << " "; // 输出2 3 4 5 6
}

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

2.2.2.3 auto &&类型 

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

vector<bool> vec {true, false, true, false};

// for (auto &a : vec) {  // 编译报错
for (auto &&a : vec) {    // 编译通过
    cout << a << " ";
}

输出:

1 0 1 0

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

vector<bool> vec {true, false, true, false};

bool &v =  vec[0]; // 编译报错, vec[0]不是一个左值
bool *w = &vec;    // 编译报错,无法将一个临时变量地址绑定给指针

auto &&u = vec[0]; // 编译通过,非常量右值引用

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

2.2.2.4 const auto&类型

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

vector<int> v{1,2,3,4,5};

for(const auto& i : v) {
    i++;              // 编译出错
    cout << i << " ";
}

 

3.使用注意 

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

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

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

#include <vector>
#include <iostream>

int main(void) {
    std::vector<int> v{ 5, 5, 0, 5, 1 };

    for(auto&& i : v) {
        std::cout << ' ' << i;
        if (5 == i) {
            v.emplace_back(123); // 追加元素后、迭代器无效
        }
    }
}

 结果:

5 5 -1978514432 -946396368 1

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

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

例子如下:

#include <initializer_list>
#include <iostream>
#include <vector>

struct something
{
	std::vector<int> v;

	something(const std::initializer_list<int>& l ) : v(l) {}
	std::vector<int>& get_vector() { return v; }
	~something() noexcept { std::cout << "destructor" << std::endl; }
};

int main()
{
	for( auto e : something { 1,2,3,4,5,6,7,8,9,0 }.get_vector() )
	{ 
		std::cout << e; // something是临时对象、此时它的的生命周期已结束
	}
	std::cout << std::endl;
}

4. 结束语

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

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值