1、  for循环的新用法

  在C++98/03中,通过for循环对一个容器进行遍历,一般有两种方法,常规的for循环,或者使用<algorithm>中的for_each方法。

  for循环遍历:

void func(void)
{
std::vector<int> arr;
for(auto it = arr.begin(); it != arr.end(); ++it)
{
std::cout << *it << std::endl;
}
}

  for_each方法:

void vFuncCall(int n)
{
std::cout << n << std::endl;
} void func2(void)
{
std::vector<int> arr;
std::for_each(arr.begin(), arr.end(), vFuncCall);
}

  for_each相比一般的for循环,只需关注容器元素的类型,但是都是基于范围的循环,必须显示的给出容器的开始(begin)和结束(end)。C++11中改善了这种遍历方式,不再需要给出容器的两端,循环会自动根据容器的范围自动的展开,在循环中屏蔽了迭代器的遍历细节,直接抽取容器中的元素进行运算。我们来看C++11中是怎么遍历容器的:

void func(void)
{
std::vector<int> arr;
for(auto n : arr)
{
std::cout << n << std::endl;
}
}

  是不是很简洁,n表示arr中的一个元素, auto会被编译器自动推导出类型,此例中被推导为vector中的int类型。当然,也可以直接写出类型:

std::vector<int> arr;
for(int n : arr)

  同时,这种循环中,冒号前面的变量支持隐式转换的,因此在使用时需要注意:

std::vector<int> arr;
for(char c : arr) //int会被隐式转换为char

  在这种循环中,我们都是只读方式来遍历容器的,如果需要改变容器中的值,我们需要加上引用,如果是只希望遍历而不是修改,我们可以使用const auto & 来定义n的类型,这样对于复制负担比较大的容器元素(std::vector<std::string>数组等)也可以无耗的进行遍历。

std::vector<int> arr;
for(auto & n : arr)
{
std::cout << n++ << std::endl; //打印,并把元素的值+1
}

2、  使用细节

2.1 推导类型

  我们来看使用范围的for循环和普通的for循环有什么区别:

std::map<std::string, int> mmsi_test = {{"", }, {"", }, {"", }};

//一般情况的for循环
for(auto it = mmsi_test.begin(); it != mmsi_test.end(); ++it)
{
std::cout << it->first << "->" << it->second << std::endl;
} //基于范围的for循环
for(auto val : mmsi_test)
{
std::cout << val.first << "->" << val.second << std::endl;
}

  可以看出:

  • for循环中的val类型是std::pair,对于map这种关联性容器来说,需要使用val.first或者val.second来提取键值;
  • auto自动推导出来容器元素的类型是value_type,而不是迭代器。

2.2 容器约束

  如果我们要改变某些容器元素的值,通过auto &可以解决大多数问题,但是某些特殊容器并不能达到我们预期的结果。比如我们希望在循环中对set的值进行修改,但是set的内部元素的值是可读的----由set容器的特性决定的,因此for循环中的auto &会被推导为const xx &。同样基于范围的map遍历一样,for循环得到的std::pair引用,是不能修改first的。

void func(void)
{
std::set<int> ss = {, , };
for(auto &val : ss)
{
//error increment of read-only refrence 'val'
std::cout << val++ << std::endl;
}
}

2.3访问频率

  我们先来看一段代码,测试一下C++11中的循环对于容器的访问频率。

#include <iostream>
#include <vector> std::vector<int> g_arr = {,,,,}; std::vector<int>& func(void)
{
std::cout << "get range->" << std::endl;
return g_arr;
} int main(void)
{
for(auto val : func())
{
std::cout << val << std::endl;
} return ;
}

  程序的执行结果:

  我们可以从结果中看出,不论基于范围的for循环迭代了多少次,func()只在第一次迭代之前被调用,在循环之前就确定好迭代的范围,而不是在每次迭代之前都去调用一次end()。所以可以得出结论:基于范围的for循环,冒号后面的表达式只会被执行一次。

  那么我们来看如果在基于范围的for循环中修改容器会出现什么情况:

#include <iostream>
#include <vector> int main(void)
{
std::vector<int> arr = {,,,,};
for(auto val : arr)
{
std::cout << val << std::endl;
arr.push_back(); //扩大容器
} return ;
}

  在centos6.7 64位系统运行结果:

  从结果看出,这并不是我们需要的结果,如果把vector换成list,结果又不一样。

  因为介于范围的for循环其实是普通for循环的语法糖,同普通的循环一样,在迭代时修改容器可能引起迭代器失效,导致一些意料之外的结果。由于我们这里是没法看到迭代器的,所以在基于范围的for循环中修改容器到底会造成什么样的影响非常困难。

C11简洁之道:循环的改善的更多相关文章

  1. C11简洁之道:初始化改进

    1.  C++98/03初始化 我们先来总结一下C++98/03的各种不同的初始化情况: //普通数组 ] = {, , }; //POD(plain old data) struct A { int ...

  2. C11简洁之道:类型推导

    1.  概述 C++11里面引入了auto和decltype关键字来实现类型推导,通过这两个关键字不仅能方便的获取复杂的类型,还能简化书写,提高编码效率. 2.  auto 2.1 auto关键字的新 ...

  3. C11简洁之道:模板改进

    1.  右尖括号 我们在C++98/03中使用泛型编程的时候,经常遇到“>>”被当作右移操作符,而不是模板参数的结尾.假如我们有如下代码: template <typename T& ...

  4. C11简洁之道:tupe元祖

    tuple元组是一个固定大小不同类型的值的集合,是泛化的std::pair.我们也可以把它当作一个通用的结构体来使用,不需要创建结构体有获取结构体特征,在某些情况可以取代结构体,使程序更简洁.直观. ...

  5. C11简洁之道:lambda表达式

    1.  定义 lambda表达式是C++11非常重要也是很常用的特性之一,来源于函数式编程的概念,也是现代编程语言的一个特点.它有如下特点: 声明式编程风格:就地匿名定义目标函数或者函数,不需要额外写 ...

  6. C11简洁之道:函数绑定

    1.  可调用对象 在C++中,有“可调用对象”这么个概念,那么什么是调用对象呢?有哪些情况?我们来看看: 函数指针: 具有operator()成员函数的类对象(仿函数): 可以被转换为函数指针的类对 ...

  7. JavaScript 代码简洁之道

    摘要: 可以说是<Clean Code>的JS代码示例了,值得参考. 原文:JavaScript 代码简洁之道 作者:缪宇 Fundebug经授权转载,版权归原作者所有. 测试代码质量的唯 ...

  8. 《Clean Code》 代码简洁之道

    作者介绍 原文作者: Robert C. Martin, Object Mentor公司总裁,面向对象设计.模式.UML.敏捷方法学和极限编程领域的资深顾问,是<敏捷软件开发:原则.模式.与实践 ...

  9. JAVA基础之代码简洁之道

    引言 普通的工程师堆砌代码,优秀的工程师优雅代码,卓越的工程师简化代码.如何写出优雅整洁易懂的代码是一门学问,也是软件工程实践里重要的一环.--来自网络 背景 软件质量,不但依赖于架构及项目管理,更与 ...

随机推荐

  1. POJ 1815 Friendship(最大流最小割の字典序割点集)

    Description In modern society, each person has his own friends. Since all the people are very busy, ...

  2. 小程序的picker的range 是一个 Object Array (对象数组)

    小程序的picker的range 是一个 Object Array (对象数组) 数据: array: [{'id':1,'name':'Android'},{'id':2,'name':'IOS'} ...

  3. java键盘IO

    public class IO { public static void main(String[] args) throws Throwable { ScannerTest(); // testSc ...

  4. Java中Collection和Collections的区别(转载)

    转载来源:http://www.cnblogs.com/dashi/p/3597937.html 1.java.util.Collection 是一个集合接口(集合类的一个顶级接口).它提供了对集合对 ...

  5. LintCode-53.翻转字符串

    翻转字符串 给定一个字符串,逐个翻转字符串中的每个单词. 说明 单词的构成:无空格字母构成一个单词 输入字符串是否包括前导或者尾随空格?可以包括,但是反转后的字符不能包括 如何处理两个单词间的多个空格 ...

  6. TCP系列28—窗口管理&流控—2、延迟ACK(Delayed Acknowledgments)

    一.简介 之前的内容中我们多次提到延迟ACK(Delayed Ack),延迟ACK是在RFC1122协议中定义的,协议指出,一个TCP实现应该实现延迟ACK,但是ACK不能被过度延迟,协议给出延迟AC ...

  7. PowerMock用法[转]

    转:http://agiledon.github.io/blog/2013/11/21/play-trick-with-powermock/ 当我们面对一个遗留系统时,常见的问题是没有测试.正如Mic ...

  8. Nautilus-Share-Message: Called "net usershare info" but it failed: Failed to

    See what nautilus processes are running : ps aux | grep nautilus Kill all nautilus processes you see ...

  9. Windows Sever 2008隐藏和系统属性

    由于有些目录为隐藏和系统属性,首先要把 显示系统文件和显示所有文件 功能开启,把隐藏文件和目录显出来. 1.C:\Windows\Web\Wall*** 自带墙纸,不需要的可以删除掉. 2.C:\Wi ...

  10. div、span绑定内容改变事件

    内容改变事件onchange只适用于form表单标签(input.select.textarea) 当需要对div.span标签进行内容改变监听则无法适用,查阅了一些资料发现jquery有针对的方法, ...