一、c++11中新增的for循环
在传统的c/c++中,for循环的形式基本就是变量的限制迭代,即使在STL中使用迭代器的本质,其实也是如此。而在c++11中则提供了语法糖的range方法。这种方法其实更好的解决了两个问题,一是对所有数据类型的兼容,不再有原生类型直接遍历,复杂类型需要使用迭代器;二是和auto结合,实现了数据的自动获取而不是原来的强制指定数据类型,应用更方便快捷。
这种语法糖,更多的是提供了一种类似数据库的查询而不是修改(关联式容器),这一点一定要注意。也就是说,不可将其用于大多数的STL中的数据结构的删除和修改,除非有明确的文档说明可以(原生类型和有些类型还是可以修改的)。这种循环方式,应用范围很广,基本上原来可以用for循环的地方都可以直接应用,甚至是返回值是可以遍历的相关数据类型的函数均可。
不过,在c/c++中,由于数组和指针可以转换,需要明确一下,指针代表的数组是不可以使用此种循环方式的。使用这种方式直接拿到相关数据结构的结果(可以是引用,也可以是值),这样的优势就在于省略了类型转换带来的风险,降低了开发者的应用难度。
二、注意事项及例程
1、不能用此种方式增加或者删除元素
void TestDel()
{
std::unordered_map<int, int> umap;
for (int i = 0; i < 10; i++)
{
umap.emplace(std::make_pair(i, i + 2));
}
//删除
for (auto& d : umap)
{
std::cout << d.first << " " << d.second << std::endl;
umap.erase(d.first);//这会引发异常
}
}
int main()
{
TestDel();
}
上面的代码会引发了异常:“ 读取访问权限冲突。d 是 0xFFFFFFFFFFFFFFFF”。
2、对auto推导的类型要保持强一致
对于一些简单的原生数据类型,可能习惯于凭直觉去做,这也没啥问题,但对一些比较复杂的STL中的数据结构,就有些问题了,比如map系列,还有一些同时支持多种类型的,也需要看到底返回提哪种类型,再做具体的数据解析:
#include <iostream>
#include <unordered_map>
void TestRange()
{
std::unordered_map<int, int> umap;
for (int i = 0; i < 10; i++)
{
umap.emplace(std::make_pair(i,i+2));
}
//看一下为什么不直接显示值
for (auto& d : umap)
{
std::cout << d.first << " " << d.second << std::endl;
}
}
int main()
{
TestRange();
}
一定要注意的是,auto返回的是一个std::pair,这样才能正确的解析数据。
3、对关联容器要明白相关约束
也就是上面提到的关联容器相关的for循环是只读的。看一下相关代码:
void TestSet()
{
std::set<int> sData;
for (int i = 0; i < 10; i++)
{
sData.emplace(i);
}
for (auto& d : sData)
{
//此处会有编译提示错误
d = 3;
}
}
int main()
{
TestSet();
}
这个Set就是典型的是一个只读的类型情况,其实包括map的Key也是无法修改的。
其实类似于网上说的在遍历函数返回的容器和字符串时,只执行一次,这个不是for循环的特点,这玩意是编译器进行的优化,同学们一定要明确。
三、总结
c/c++的难度就在于此,到处都是一些小细节,一个把握不好,或者是编程的风格不好,就被带进坑里了。或者说,c/c++对初学者是不友好的。好多刚刚入门的c++程序员,面对着Bug无从下手,甚至有些人说原来这么写,跑越来没问题啊。不是没问题,是工程是Demo性质,错误还来不及显示就退出了。到了真正的工程上,很快就暴露出来了。希望新的c++标准能不断的推陈出新,让更多的人愿意学习c++,让c++能够更有活力。
大道至简,大约如是!