#include <iostream>
using namespace std;
#include<vector>
#include<map>
#include<set>
//在C++98/03中,不同的容器和数组遍历的方式不尽相同,写法不统一,也不简洁,而c++11基于范围的
//for循环可以以简洁,统一的方式来遍历容器和数组,用起来也更方便了。
//旧的for循环遍历方法
void test()
{
vector<int> t{1,2,3,4,5,6};
for(auto it=t.begin();it!=t.end();++it)
{
cout<<*it<<" ";
}
cout<<endl;
//我们在遍历的过程中需要给出容器的两端:开头(begin)和结尾(end),因为这种遍历方式不是基于范围来设计的。在基于范围的for循环中,不需要再传递容器的两端,循环会自动以容器为范围展开,并且循环中也屏蔽掉了迭代器的遍历细节,直接抽取容器中的元素进行运算,使用这种方式进行循环遍历会让编码和维护变得更加简便。
//c++11基于范围的for循环,语法格式:for(declaration:expression){ /循环体}
//c++11遍历方法
for(auto value:t)
{
cout<<value<<" ";
}
cout<<endl;
//在上面的例子中,是将容器中遍历的当前元素拷贝到了声明的变量value中,因此无法对容器中的元素进行写操作,如果需要在遍历过程中修改元素的值,需要使用引用。
vector<int> t2{1,2,3,4,5,6};
for(auto &value:t2)
{
cout<<value++<<" ";
}
cout<<endl<<"遍历修改之后的容器:";
for(auto &value:t2)
{
cout<<value<<" ";
}
cout<<endl;
}
//2使用细节
//2.1关系型容器
void test2()
{
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;
}
}
// 在上面的例子中使用两种方式对 map 进行了遍历,通过对比有两点需要注意的事项:
// 使用普通的 for 循环方式(基于迭代器)遍历关联性容器, auto 自动推导出的是一个迭代器类型,需要使用迭代器的方式取出元素中的键值对(和指针的操作方法相同):
// it->first
// it->second
// 使用基于访问的 for 循环遍历关联性容器,auto 自动推导出的类型是容器中的 value_type,相当于一个对组(std::pair)对象,提取键值对的方式如下:
// it.first
// it.second
//2.2元素只读
//通过对基于范围的 for 循环语法的介绍可以得知,在 for 循环内部声明一个变量的引用就可以修改遍历的表达式中的元素的值,但是这并不适用于所有的情况,对应 set 容器来说,内部元素都是只读的,这是由容器的特性决定的,因此在 for 循环中 auto & 会被视为 const auto & 。
void test3()
{
set<int> st{ 1,2,3,4,5,6 };
for (auto &item : st)
{
// cout << item++ << endl; // error, 不能给常量赋值
}
}
//2.3访问次数
//基于范围的 for 循环遍历的对象可以是一个表达式或者容器 / 数组等。假设我们对一个容器进行遍历,在遍历过程中 for 循环对这个容器的访问频率是一次还是多次呢?我们通过下面的例子验证一下:
vector<int> v{1,2,3,4,5,6};
vector<int>&getRange()
{
cout<<"get vector range..."<<endl;
return v;
}
void test4()
{
for(auto val:getRange())
{
cout<<val<<" ";
}
//结果是getRange只被调用了一次,及不论基于范围的for循环迭代了多少次,函数getRange()
//只在第一次迭代之前被调用,得到这个容器对象之后就不会再去重新获取这个对象了。
cout<<endl;
}
//对应基于范围的 for 循环来说,冒号后边的表达式只会被执行一次。在得到遍历对象之后会先确定好迭代的范围,基于这个范围直接进行遍历。如果是普通的 for 循环,在每次迭代的时候都需要判断是否已经到了结束边界。
int main() {
test();
test2();
test4();
return 0;
}