C++ 头文件系列(iterator)
简介
该头文件围绕迭代器展开,定义了一系列与迭代器有关的概念,但最最最重要的一点就是----它和其它容器一起实现了C++容器的Iterator设计模式。
Iterators are a generalization of pointers that allow a C++ program to work with different data structures(containers) in a uniform manner.
上述文字摘自C++14标准草案,简而言之,迭代器就是对指针的一层封装,提供了统一的接口。
使用迭代器有很多好处:
- 访问数据内容,同时不暴露其内部结构,降低耦合性。
- 支持multiple traversal(即同时有多个遍历发生)。
- 提供统一的访问接口和多态遍历(该多态为静态多态,发生在编译期)。
详细请见设计模式。
迭代器类别
迭代器主要有5类([iterator-class]代指该类迭代器支持的操作集):
这里有两点需要特别说明:
- multi-pass:它的意思是支持同时多次个遍历 (这个概念有待验证)。
- 解引用:该操作是有限制的,只能出现在赋值语句的左边。
不难看出,这几类迭代器有如下关系:
迭代器 与 指针
因为迭代器实际上是指针的抽象,很多功能概念都是从指针身上“扒”下来的,所以它的语义跟指针是一致的。
这意味着什么呢? 这意味着可以传入指针作为迭代器, 因为指针上的操作集(递增、递减、算数运算等)是迭代器的超集,模版定义对迭代器所做出的操作要求放在指针上完全适用。
这在操纵内置数组的时候,可以省去不少麻烦(不用再去敲多余的代码来生成iterators):
int numbers[] = { 1,2,3,4 };
std::find(numbers, numbers + 4, 2);
迭代器基础设施
标准库提供了以下4个方面的设施来帮助用户使用iterator。
iterator_traits类
“traits”是特性的意思,所以“iterator_traits”是迭代器特性的意思。 从代码角度看,这里的traits就是types,因为这个类只包含了五个类型定义:
difference_type
value_type
reference
pointer
iterator_category
因为algorithm在C++是单独的一块,是iterator将容器与算法沟通在一起。 也就是说,标准库的算法只是通过迭代器来进行数据操作。 必然而然的,一些操作需要有对应的类型: 1) 例如,应用distance库函数计算迭代器的距离,应该返回“距离”类型的值。 2) 例如,获取迭代器指向的对象,应该返回“对象值”类型的对象。 等等...
所以,标准库的算法需要我们定义这些类型,好让它在应用算法的时候使用正确的类型。
需要注意的是,当迭代器为output iterators时,上面的4个类型可能被定义为void(可能对于output iterator来说,这四个类型都没有多大意义,它支持的操作非常有限)。
iterator类
上面这个iterator_traits类取自某个库的iterator实现,可以看到,默认的iterator_traits模版内的类型定义都取自迭代器中相应的类型,即_Iter迭代器类。 所以我们在定义自己的迭代器的时候,如果定义了这些类,就不用再显示实例化iterator_traits模版了,它能自动提取出这些类型。
这时候就轮到我们的iterator类来大显身手啦! 用户只要继承这个base class并指定两个参数,就可以获得剩余的三个类型定义,因为它的定义是这样的:
iterator category tags
对应迭代器类别,这里也有5类标签(tag),名称为XXX_tag,XXX对应迭代器名称。
这个标签的作用主要是实现标签派发功能,提供迭代器类型信息,从而让C++库算法可以选择合适的、高效的操作来完成算法(可参见下一小节)。
iterator functions
头文件还提供了一些方便的操纵迭代器的函数供用户使用:
- advance(步进)
- distance(距离)
- next(下一个)
- prev(上一个):只支持双向迭代器以上(因为单向迭代器不支持“--”操作符)。
对于不同类型的迭代器,上述四个函数采用不同的方法进行计算,例如:
- 随机访问迭代器支持算数运算,故使用“+”和“-”操作符进行运算(若两个迭代器分别为first和last,则计算他们之间的距离只需要last - first)。
- 单向迭代器只支持递增运算,故使用++运算符进行运算(若两个迭代器分别为first和last,计算他们之间的距离需要重复对first进行递增,知道first == last)
迭代器适配器
类
标准库包含了三种迭代器适配器:
- Reverse iterator:这种迭代器对元素进行反向迭代。 注意,当从某个迭代器构造出reverse iterator时,新的迭代器不再指向先前的元素,而是指向前一个元素(按旧迭代器的顺序),因为end iterator逆转过来才是begin iterator。
Insert iterator:通过迭代器进行元素的插入时,操作略有不同。 指针通常是指向已有的内存,因此迭代器一般也只是对指向地址的元素进行赋值;而插入元素是需要先分配内存,再赋值。 为了能让使用者像使用一般迭代器那样进行元素的插入,标准库提供了3种插入迭代器:
- back_insert_iterator
- front_insert_iterator
- insert_iterator
Move iterator:移动迭代器会将内部的迭代器的操作返回值全部转换成右值(rvalue)。
生成函数(Generators)
- back_inserter
- front_inserter
- inserter
- make_move_inserter
这些都是全局模版函数,利用类型推导帮助用户构造上述的三种迭代器适配器。
流迭代器
输入输出一直是语言非常重要的部分,对于C++迭代器来说,操纵流(stream)中数据的输入输出的重要性毋庸置疑。 Stream Iterator则是针对stream的一套迭代器,包括istream,ostream,istreambuf 和 ostreambuf。
以下两种是input iterator:
- istream_iterator
- istreambuf_iterator
以下两种是output iterator:
- ostream_iterator
- ostreambuf_iterator
C++ 头文件系列(iterator)的更多相关文章
- C++ 头文件系列(array)
注意,该头文件仅在C++11中标准才开始出现. 简介 与语言内置的数组一样, array类模版支持几乎所有内置数组包含的特性: 顺序的(sequence) 内存连续的(contiguous stora ...
- C++ 头文件系列(queue)
简介 这个头文件定义了两个跟队列有关的类----quque.priority_queue,分别实现的是队列 和 优先队列这两个概念. 但是与这两个类模版与其它类模版(vector.array等)最大的 ...
- C++ 头文件系列(stack)
简介 该头文件只含有一个类模版stack, 它实现栈的概念,是一个容器适配器(说实话,在写这篇随笔之前我都不知道有这么个类模版). 栈 栈只有一个重要的特性: LIFO(last-in first-o ...
- C++ 头文件系列(vector)
简介 vector头文件包含vector的类模版以及该模版的显示特化版本vector< bool >. vector是C++容器库中非常通用的一种容器,如果你不知道该决定使用哪一种容器,或 ...
- C++ 头文件系列(map)
简介 该头文件包含两个概念相似的容器----map.multimap. 而这两个容器反映的概念就是 映射. 这两个容器 相同 的属性有: 关联性 映射 动态增长 键(Key)唯一性 这两个不相同的属性 ...
- C++ 头文件系列(unordered_map、unordered_set)
简介 很明显,这两个头文件分别是map.set头文件对应的unordered版本. 所以它们有一个重要的性质就是: 乱序 如何乱序 这个unorder暗示着,这两个头文件中类的底层实现----Hash ...
- C++ 头文件系列 (algorithm)
简介 algorithm头文件是C++的标准算法库,它主要应用在容器上. 因为所有的算法都是通过迭代器进行操作的,所以算法的运算实际上是和具体的数据结构相分离的 ,也就是说,具有低耦合性. 因此,任何 ...
- C++ 头文件系列 (bitset)
简介 该头文件有关位集,实际上是vector 位 位本质上对应bool的概念,只有0或1,true或false两种对立的值. 但很可惜,字节才是机器上最小的存储单元,所以bool基本上是由一个字节大小 ...
- C++ 头文件系列(iosfwd)
简介 输入输出历来都是语言的重要部分,在C++中,该库也是占据了相当大的一部分. C++的输入输出库是其遵循面向对象设计的结果,并结合了泛型编程. 以下是这些库类的关系图(箭头标示继承,白框表示摸板, ...
随机推荐
- sort用法
一.sort用法sort将文件的每一行作为一个单位,相互比较,比较原则是从首字符向后,依次按ASCII码值进行比较,最后将他们按升序输出.vim 1.txt 1:datadir=/aaa/zzz:2: ...
- 结对编程--Goldpoint Game
黄金点游戏 黄金点游戏描述: N个同学(N通常大于10),每人写一个0~100之间的有理数 (不包括0或100),交给裁判,裁判算出所有数字的平均值,然后乘以0.618(所谓黄金分割常数),得到G值. ...
- [Unity]C#中 将XML和实体类之间进行相互转换的工具类
using System; using System.Xml; using System.Xml.Serialization; using System.IO; namespace LOTool { ...
- memcache细节解析
转自:原链接 Memcached内存管理采取预分配.分组管理的方式,分组管理就是划分slab class,按照chunk的大小slab被分为很多种类. slab Slab是一个内存块,它是memc ...
- 《算法导论》习题2.3-5 二分搜索 Binary Search
地球人都知道“二分查找”,方法也非常简单,但是你能不能在10分钟内写出一个没有bug的程序呢? 知易行难,自己动手写一下试一试吧. public class BinarySearch { public ...
- ucos2.86的任务调度漏洞
Ucos2.86版本有一个任务调度的漏洞,该漏洞在2.88之后的版本已经修改过来了,今天我们来看看这个漏洞, 漏洞在官方2.88的文档中如下 这两个函数都是调度器函数,也就是说调度器有漏洞,但是看官方 ...
- Hibernate 中对象关系映射(ObjectRelationMapping)
1.什么是对象关系映射? 解析:对象-关系映射(Object Relational Mapping,简称ORM,对象关系映射)是一种为了解决面向对象与关系数据库存在的互不匹配的现象的技术. 简单的说, ...
- tp框架命名空间
命名空间:相当于虚拟的目录在tp里面主要为了实现自动加载类 TP框架下有一个初始命名空间(相当于根目录)初始命名空间:ThinkPHP\Library 在初始命名空间下又包含很多根命名空间这些根命名空 ...
- 2)Javascript设计模式:Singleton模式
Singleton模式 var User = (function() { var instance; function _User(){} _User.prototype.say = function ...
- Linux服务器开发/测试环境搭建-流程
1.MariaDB yum 安装/初始化/授远程权限 yum安装 在MariaDB官网根据Linux系统查找您所需要的db版本:https://downloads.mariadb.org/mariad ...