C11简洁之道:循环的改善
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简洁之道:循环的改善的更多相关文章
- C11简洁之道:初始化改进
1. C++98/03初始化 我们先来总结一下C++98/03的各种不同的初始化情况: //普通数组 ] = {, , }; //POD(plain old data) struct A { int ...
- C11简洁之道:类型推导
1. 概述 C++11里面引入了auto和decltype关键字来实现类型推导,通过这两个关键字不仅能方便的获取复杂的类型,还能简化书写,提高编码效率. 2. auto 2.1 auto关键字的新 ...
- C11简洁之道:模板改进
1. 右尖括号 我们在C++98/03中使用泛型编程的时候,经常遇到“>>”被当作右移操作符,而不是模板参数的结尾.假如我们有如下代码: template <typename T& ...
- C11简洁之道:tupe元祖
tuple元组是一个固定大小不同类型的值的集合,是泛化的std::pair.我们也可以把它当作一个通用的结构体来使用,不需要创建结构体有获取结构体特征,在某些情况可以取代结构体,使程序更简洁.直观. ...
- C11简洁之道:lambda表达式
1. 定义 lambda表达式是C++11非常重要也是很常用的特性之一,来源于函数式编程的概念,也是现代编程语言的一个特点.它有如下特点: 声明式编程风格:就地匿名定义目标函数或者函数,不需要额外写 ...
- C11简洁之道:函数绑定
1. 可调用对象 在C++中,有“可调用对象”这么个概念,那么什么是调用对象呢?有哪些情况?我们来看看: 函数指针: 具有operator()成员函数的类对象(仿函数): 可以被转换为函数指针的类对 ...
- JavaScript 代码简洁之道
摘要: 可以说是<Clean Code>的JS代码示例了,值得参考. 原文:JavaScript 代码简洁之道 作者:缪宇 Fundebug经授权转载,版权归原作者所有. 测试代码质量的唯 ...
- 《Clean Code》 代码简洁之道
作者介绍 原文作者: Robert C. Martin, Object Mentor公司总裁,面向对象设计.模式.UML.敏捷方法学和极限编程领域的资深顾问,是<敏捷软件开发:原则.模式.与实践 ...
- JAVA基础之代码简洁之道
引言 普通的工程师堆砌代码,优秀的工程师优雅代码,卓越的工程师简化代码.如何写出优雅整洁易懂的代码是一门学问,也是软件工程实践里重要的一环.--来自网络 背景 软件质量,不但依赖于架构及项目管理,更与 ...
随机推荐
- 针对XX系统的可用性和易用性构想
可用性是与系统故障有关的一个质量属性,是指系统正常运行的时间的比例,一般通过两次故障之间的时间长度或在系统崩溃情况下能恢复正常运行的速度来衡量,同时此概念涉及一个公式的计算,就是系统正常运行时间的百分 ...
- “今日校园” App 用户体验分析
一.背景 为进一步提升信息化应用水平,更好的服务师生,南通大学智慧校园移动端APP“今日校园”定于11月5日正式上线运行.登陆APP可浏览学校新闻.校园生活.各部门微信公众号等内容,查看校内通知.校内 ...
- 哈希表 STL map
计数排序时我们使用一个数组来记录出现的数字的次数,而当数据范围太大时,无法建立一个那么大地数组(而且可能空间利用率很低,太浪费),此时可以改用hash table . binary search tr ...
- 二叉搜索树(BST)---python实现
github:代码实现 本文算法均使用python3实现 1. 二叉搜索树定义 二叉搜索树(Binary Search Tree),又名二叉排序树(Binary Sort Tree). 二叉搜 ...
- Swift-(OC中的enumerateObjectsUsingBlock跟Swift的enumerate区别)
OC中使用: NSArray * lists = [NSArray array]; [lists enumerateObjectsUsingBlock:^(id _Nonnull obj, NSUI ...
- anaconda安装不存在的包
Anaconda作为一个工具包集成管理工具,下载python工具包是很方便的,直接敲: conda install package_name 1 但是有时候安装一个工具包(如skimage)的时候,在 ...
- touchSwipe 上下左右滑动,二指缩放 效果不好。
$(function(){ var _showImgW; var _showImgH; var _showImgMT; var _showImgML; $("#imgDiv").s ...
- Linux命令发送Http GET/POST请求
Get请求 curl命令模拟Get请求: 1.使用curl命令: curl "http://www.baidu.com" 如果这里的URL指向的是一个文件或者一幅图都可以直接下载到 ...
- matlab 并行运算【转】
一.Matlab并行计算原理梗概 Matlab的并行计算实质还是从主从结构的分布式计算.当你初始化Matlab并行计算环境时,你最初的Matlab进行自动成为主节点,同时初始化多个(具体个数手动设定, ...
- RT-thread 设备驱动组件之SPI设备
本文主要介绍RT-thread中的SPI设备驱动,涉及到的文件主要有:驱动框架文件(spi_dev.c,spi_core.c,spi.h),底层硬件驱动文件(spi_hard.c,spi_hard.h ...