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. wpa_supplicant上行接口浅析

    摘自http://blog.csdn.net/fxfzz/article/details/6176414 wpa_supplicant提供的接口 从通信层次上划分, 上行接口:wpa_supplica ...

  2. 【APS.NET Core】- Razor Page 使用jqgrid实现分页功能

    本文将使用jqgrid在Razor Page中实现分页功能. 前台 List.cshtml代码如下: @page @model ListModel @{ Layout = "~/Pages/ ...

  3. 优化mysql的内存

    Mysql占用CPU过高的时候,该从哪些方面下手进行优化? 占用CPU过高,可以做如下考虑:1)一般来讲,排除高并发的因素,还是要找到导致你CPU过高的哪几条在执行的SQL,show processl ...

  4. C# 知识回顾 - 表达式树 Expression Trees

    C# 知识回顾 - 表达式树 Expression Trees 目录 简介 Lambda 表达式创建表达式树 API 创建表达式树 解析表达式树 表达式树的永久性 编译表达式树 执行表达式树 修改表达 ...

  5. Winform程序部署方式总结一——ClickOnce发布

    针对Winform程序,介绍两种常用打包方式:ClickOnce和Windows Installer 应用程序如下: 一.ClickOnce发布 1.找到需要发布的项目文件,右击,从弹出的快捷菜单中找 ...

  6. Bootstrap如何适配移动浏览器

    移动设备优先 1.由meta标签决定的 <meta name="viewport" content="width=device-width, initial-sca ...

  7. 【Python】python和json数据相互转换,json读取和写入,repr和eval()使用

    python数据转换json 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import jso ...

  8. 关于__name__=='__main__

    if __name__=='__main__' :  为了区分你是主动执行这个脚本,还是从别的地方把它当做一个模块去调用. 如果是主动执行,则执行.如果是调用的,则不执行主体. 里面存放的可能是一些测 ...

  9. 源码安装和yum安装的区别。

    yum是将yum源中别人已经编译好的rpm包下载到本地,然后安装,不需要考虑依赖,主要是方便.源码安装没法人为的控制,安装的版本也很低. 源码安装需要自己编译,安装,编译过程中可以设置参数.可安装的版 ...

  10. 【题解】洛谷P4735最大异或和

    学习了一下可持久化trie的有关姿势~其实还挺好理解的,代码也短小精悍.重点在于查询某个历史版本的trie树上的某条边是否存在,同样我们转化到维护前缀和来实现.同可持久化线段树一样,我们为了节省空间继 ...