接触迭代器主要是在接触和使用STL时候开始的。作为泛型编程的良好实践,STL的迭代器有着非常显著的代表性。
最初,我并不了解迭代器的作用,甚至一直很排斥使用它。但随着逐步开始了解STL,慢慢开始了解了迭代器的意义以及重要性。本文主要是就自己所学所了解做一个简单的整理。本文内容参考整理自《泛型编程与STL》。
一、迭代器的重要性
迭代器,又称作泛型指针。顾名思义,迭代器就是模仿指针来实现的。有些情况,它就是指针。比方说vector,由于它使用的是一整块连续空间,所以直接拿其指针就可以作为随机迭代器使用,是非常强力的。
那么为什么我们不直接用指针,而一定要弄一个迭代器出来呢?
因为它是数据结构与算法之间的重要接口。以STL中的算法find为例,它要求可以遍历某个数据结构并读取其中元素的值。如果我们只提供指针给它,那么它针对vector要写一个算法,针对list链表又要重新写一个算法。而实际上,算法对这两种数据结构的要求是相同的:按某个线性顺序遍历并读取元素。因此我们可以把vector和list的遍历的这种特性抽象出来,做成迭代器,这样算法就可以保证它的通用性和简洁性、有效性。对于vector的抽象很简单,只需要把其指针拿出来直接用即可。但对于list,我们就需要在iterator类中对++操作、*操作进行运算符重载,以模仿指针行为。
对于不同的数据结构,迭代器的复杂程度是截然不同的,而能实现的功能也是不同的。与此同时,我们也要注意到不同的算法对迭代器的要求也是不同的。因此,我们要了解迭代器的分类,以及不同的迭代器和什么样的算法是匹配的,这样才能准确的使用STL库,甚至设计自己的迭代器。
二、迭代器的分类
迭代器总的来说,主要有五种不同的迭代器类别:Input Iterator,Output Iterator,Forward Iterator,Bidirectional Iterator,Random Access Iterator。此处暂且不谈Trivial Iterator,其存在的目的是为了用来厘清其他Iterator concepts的定义。
1,Input Iterator
①作为一般的指针来看的时候,有三种取值,可取值指针,跨越尾端指针,NULL。
②可以比较相等性。(比较大小是无意义的)
③可以被复制或被赋值,即可以读取出来。
④可以提领对象,即*运算是有意的。(*p)
⑤支持++操作,指向下一个对象。(++p,p++)
⑥不能改动其提领的结果。(即*p=x不一定有效)
⑦遍历时,不保证之前的迭代器的有效性。单回(single pass)算法适用。
事实上,这个名字暗示了我们用这个迭代器读取下一个值之后,上一个会消失。一个很典型的代表就是STL中的Input Iterator class istream_iterator,可以很简单地从input stream读进数据。
2,Output Iterator
与Input Iterator类似,只是由读取变为了写入。
①同上,除了跨越尾端指针这处。
②同上
③可以复制和赋值,即可以写入。
④可以提领对象方式复制。(*p=x)
⑤同上
⑥不能读取其值。(不能用x=*p)
⑦同上
和上面类似,在STL中一个很典型的代表就是ostream_iterator,可以很简单地写入数据到ostream。
3,Forward Iterator
①同时具备读写性
②可以同时对多个元素操作(支持multipass算法)
单向迭代器在上面两种迭代器上进行了更强的功能约束。在单向遍历上,我们更常用这种迭代器。以stl的find为例,虽然它的要求只是Input Iterator即可以使用,但我们更多的时候是希望查找并且不改变原有数据的状态的,所以我们多数时候使用的都是单向迭代器。
4,Bidirectional Iterator
①支持双向移动,重载--运算(--p.p--)
相对上一个增强了反向移动的功能。双向链表就是这样的迭代器。
5,Random Access Iterator
①支持加法、减法(p+n,p-n)
②支持下表运算(p[n])
③支持相减(p1-p2)
④支持比较前后次序(p1 随机迭代器是最强化的迭代器,功能的约束也最高,它实现了所有的指针运算。vector的迭代器就属于这类。这种迭代器对于STL中的sort这类算法非常重要,因为sort中的排序算法要求能随机读取数据。 三、迭代器之间的关系 定义Refinement(精炼、强化):如果concept C2提供concept C1的所有功能,再加上其他可能的额外功能,就说C2是C1 的refinement。 性质: 1,Reflexivity(自反性)。每个concept C 都是自身的一个refinement。 2,Containment(涵盖性)。如果x 是concept C2的一个model,而C2是C1的一个refinement,那么x必然是C1的一个model。 3,Transitivity(传递性)。如果C3是C2的一个refinement,C2是C1的一个refinement,那么C3一定是C1的refinement。 四、部分常用数据结构、算法与迭代器 下面简单的介绍一下我们平时常用的一些数据结构的迭代器类型。 序列式容器: vector 提供随机迭代器(插入、删除元素都会导致迭代器失效) list 提供单向迭代器(插入不会使迭代器失效,删除非指向元素不会使迭代器失效) deque 提供随机迭代器(插入、删除元素都会导致迭代器失效) string 提供随机迭代器(插入、删除元素都会导致迭代器失效) 关联式容器: set 提供双向迭代器 (如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。) map 提供双向迭代器(迭代器只能修改value,不能修改key的值。如果迭代器所指向的元素被删除,则该迭代器失效。其它任何增加、删除元素的操作都不会使迭代器失效。) 其他容器: stack、queue、bitset 不能遍历。 不改变操作对象内容的算法: Input Iterator:find,find_if ,find_first_of,count,,count_if,for_each,equal,mismatch,lexicographical_compare, Forward Iterator:adjacent_find,search,find_end,search_n,min_element,max_element 会改变操作对象内容的算法: Input Iterator :accumulate,inner_product Output Iterator: fill_n,generate_n,unique_copy Input Iterator & Output Iterator: copy,transform,replace_copy,replace_copy_if,remove_copy,remove_copy_if,partial_sum,adjacent_difference Forward Iterator:swap_ranges,replace,replace_if,fill,generate,remove,remove_if,unique,rotate,rotate_copy,stable_partition Bidirectional Iterator:copy_backward,reverse,reverse_copy,next_permutation,prev_permutation,partition Random Access Iterator:random_shuffle Input Iterator &Random Access Iterator:random_sample Forward Iterator &Random Access Iterator:random_sample_n 排序和查找: Input Iterator:includes Input Iterator & Output Iterator: merge,set_union,set_intersection,set_difference,set_symmetric_difference Forward Iterator:is_sorted,binary_search,lower_bound,upper_bound,equal_range Bidirectional Iterator: inplace_merge Random Access Iterator:sort,stable_sort,partial_sort,nth_element,make_heap,push_heap,,pop_heap,sort_heap,is_heap Input Iterator &Random Access Iterator:partial_sort_copy 五、最后 整理了这么多,但依然有很多关于迭代器的概念没有整理到。比方说迭代器的其他概念:const Iterator,reverse Iterator等。还有迭代器特征、相关型别的问题也没有进行深入学习整理,留待以后有空再做整理。