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

简介: 【C++11】 基于范围的for循环

1. C++98/03 for循环与 C++ 11 for循环

1.1 两种for循环语法

在 C++03/98 中,不同的容器和数组,遍历的方法不尽相同,写法不统一,也不够简洁,而 C++11 基于范围的 for 循环以统一、简洁的方式来遍历容器和数组,用起来更方便了。

C++98/03 中普通的 for 循环,语法格式:

for(表达式 1; 表达式 2; 表达式 3)
{
    // 循环体
}

C++11 基于范围的 for 循环,语法格式:

for (declaration : expression)
{
    // 循环体
}

在上面的语法格式中 declaration 表示遍历声明,在遍历过程中,当前被遍历到的元素会被存储到声明的变量中。expression 是要遍历的对象,它可以是表达式、容器、数组、初始化列表等

1.2 两种for循环示例

普通for循环:

#include <iostream>
#include <vector>
int main(void)
{
    std::vector<int> arr;
    // ...
    for(auto it = arr.begin(); it != arr.end(); ++it)
    {
        std::cout << *it << std::endl;
    }
    return 0;
}

C++ 11 for循环

#include <iostream>
    #include <vector>
    int main(void)
    {
        std::vector<int> arr = { 1, 2, 3 };
        // ...
        for(auto n : arr)  //使用基于范围的for循环
        {
            std::cout << n << std::endl;
        }
        return 0;
    }

我们在遍历的过程中需要给出容器的两端:开头(begin)和结尾(end),因为这种遍历方式不是基于范围来设计的。在基于范围的for循环中,不需要再传递容器的两端,循环会自动以容器为范围展开,并且循环中也屏蔽掉了迭代器的遍历细节,直接抽取容器中的元素进行运算,使用这种方式进行循环遍历会让编码和维护变得更加简便。

2. 基于范围的for循环使用细节

2.1 关系型容器

使用基于范围的 for 循环有一些需要注意的细节,先来看一下对关系型容器 map 的遍历:

#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(void)
{
    map<int, string> m{
        {1, "lucy"},{2, "lily"},{3, "tom"}
    };
    // 基于范围的for循环方式
    for (auto& it : m)
    {
        cout << "id: " << it.first << ", name: " << it.second << endl;
    }
    // 普通的for循环方式
    for (auto it = m.begin(); it != m.end(); ++it)
    {
        cout << "id: " << it->first << ", name: " << it->second << endl;
    }
    return 0;
}

在上面的例子中使用两种方式对 map 进行了遍历,通过对比有两点需要注意的事项:

1.使用普通的 for 循环方式(基于迭代器)遍历关联性容器, auto 自动推导出的是一个迭代器类型,需要使用迭代器的方式取出元素中的键值对(和指针的操作方法相同):

  • it->first
  • it->second
    2.使用基于访问的 for 循环遍历关联性容器,auto 自动推导出的类型是容器中的 value_type,相当于一个对组(std::pair)对象,提取键值对的方式如下:
  • it.first
  • it.second

2.2 元素只读

先看一个例子:

#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
    vector<int> t{ 1,2,3,4,5,6 };
    for (auto value : t)
    {
        cout << value << " ";
    }
    cout << endl;
    return 0;
}

在上面的例子中,是将容器中遍历的当前元素拷贝到了声明的变量 value 中,因此无法对容器中的元素进行写操作,如果需要在遍历过程中修改元素的值,需要使用引用。

#include <iostream>
#include <vector>
using namespace std;
int main(void)
{
    vector<int> t{ 1,2,3,4,5,6 };
    cout << "遍历修改之前的容器: ";
    for (auto &value : t)
    {
        cout << value++ << " ";
    }
    cout << endl << "遍历修改之后的容器: ";
    for (auto &value : t)
    {
        cout << value << " ";
    }
    cout << endl;
    return 0;
}

在 for 循环内部声明一个变量的引用就可以修改遍历的表达式中的元素的值,但是这并不适用于所有的情况,对应 set 容器来说,内部元素都是只读的,这是由容器的特性决定的,因此在 for 循环中 auto & 会被视为 const auto & 。

#include <iostream>
#include <set>
using namespace std;
int main(void)
{
    set<int> st{ 1,2,3,4,5,6 };
    for (auto &item : st) 
    {
        cout << item++ << endl;   // error, 不能给常量赋值
    }
    return 0;
}

除此之外,在遍历关联型容器时也会出现同样的问题,基于范围的for循环中,虽然可以得到一个std::pair引用,但是我们是不能修改里边的first值的,也就是key值。

#include <iostream>
#include <string>
#include <map>
using namespace std;
int main(void)
{
    map<int, string> m{
        {1, "lucy"},{2, "lily"},{3, "tom"}
    };
    for (auto& item : m)
    {
        // item.first 是一个常量
        cout << "id: " << item.first++ << ", name: " << item.second << endl;  // error
    }
    return 0;
}

2.3 访问次数

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

#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;
}

输出的结果如下:

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

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

参看链接:

http://c.biancheng.net/view/3738.html

https://subingwen.com/cpp/for/

相关文章
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
1月前
|
程序员 C++
C++编程:While与For循环的流程控制全解析
总结而言,`while`循环和 `for`循环各有千秋,它们在C++编程中扮演着重要的角色。选择哪一种循环结构应根据具体的应用场景、循环逻辑的复杂性以及个人的编程风格偏好来决定。理解这些循环结构的内在机制和它们之间的差异,对于编写高效、易于维护的代码至关重要。
51 1
|
1月前
|
存储 编译器 程序员
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(二)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
3月前
|
存储 程序员 C++
【C++小知识】基于范围的for循环(C++11)
【C++小知识】基于范围的for循环(C++11)
|
4月前
|
存储 安全 编译器
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
【C++入门 四】学习C++内联函数 | auto关键字 | 基于范围的for循环(C++11) | 指针空值nullptr(C++11)
|
5月前
|
算法 程序员 编译器
C++的四类循环分享
C++的四类循环:Entry or Exit controlled, Ranged-based or For_each
|
5月前
|
C++
C++一分钟之-循环结构:for与while循环
【6月更文挑战第18天】在C++中,`for`循环适合已知迭代次数,如数组遍历;`while`循环适用于条件驱动的未知次数循环。`for`以其初始化、条件和递增三部分结构简洁处理重复任务,而`while`则在需要先检查条件时更为灵活。常见错误包括无限循环和逻辑错误,解决办法是确保条件更新和正确判断。了解两者应用场景及陷阱,能提升代码效率和可读性。
65 6
|
5月前
|
C语言 C++ 容器
c++primer plus 6 读书笔记 第五章 循环和关系表达式
c++primer plus 6 读书笔记 第五章 循环和关系表达式
|
5月前
|
程序员 编译器 C++
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
探索C++语言宝库:解锁基础知识与实用技能(类型变量+条件循环+函数模块+OOP+异常处理)
47 0
|
5月前
|
存储 安全 编译器
【C++】:函数重载,引用,内联函数,auto关键字,基于范围的for循环,nullptr关键字
【C++】:函数重载,引用,内联函数,auto关键字,基于范围的for循环,nullptr关键字
36 0