迭代器(iterator)是连接容器和算法的纽带,为数据提供了抽象,使写算法的人不必关心各种数据结构的细节。迭代器提供了数据访问的标准模型——对象序列,使对容器更广泛的访问操作成为可能。
//////////
泛型编程的关键所在,就是如何找到一种通用的方法,来访问具有不同结构的各种容器中的每个元素,而这正是迭代器的功能。迭代器是一种广义的指针,是指向序列元素指针概念的一种抽象。迭代器可以指向容器中的任意元素,还能遍历整个容器。
////
泛型编程的代表作品STL是一种高效、泛型、可交互操作的软件组件。STL以迭代器 (Iterators)和容器(Containers)为基础,是一种泛型算法(Generic Algorithms)库,容器的存在使这些算法有东西可以操作。
STL包含各种泛型算法(algorithms)、泛型迭代器(iterators)、泛型容器(containers)以及函数对象(function objects)。
STL并非只是一些有用组件的集合,它是描述软件组件抽象需求条件的一个正规而有条理的架构。
泛型的第一个好处是编译时的严格类型检查。这是集合框架最重要的特点。此外,泛型消除了绝大多数的类型转换。如果没有泛型,当你使用集合框架时,你不得不进行类型转换。
关于泛型的理解可以总结下面的一句话,它是把数据类型作为一种参数传递进来
////
注意:
一:迭代器作为广义的指针的概念
二:begin();和end();作为迭代器所在位置;也就是说a.end();是在所有的对象的后面的,begin();是在对象序列的开头的;
(1)特征与操作
l 迭代器的基本特征有:
n 解除——支持解除引用(dereference)操作,以便可以访问它引用的值。即,如果p是一个迭代器,则应该对*p和p->进行定义(似指针);
n 赋值——可将一个迭代器赋给另一个迭代器。即,如果p和q都是迭代器,则应该对表达式p=q进行定义;
n 比较——可将一个迭代器与另一个迭代器进行比较。即,如果p和q都是迭代器,则应该对表达式p==q和p!=q进行定义;
n 遍历——可以使用迭代器来遍历容器中的元素,这可以通过为迭代器p定义++p和p++操作来实现。
l 迭代器的操作有:
n 读——通过解除引用*来间接引用容器中的元素值,例如x = *p;
n 写——通过解除引用*来给容器中的元素赋值,例如*p = x;
n 访问——通过下标和指向引用容器中的元素及其成员,例如p[2]和p->m
n 迭代——利用增量和减量运算(++和--、+和-、+=和-=)在容器中遍历、漫游和跳跃,例如p++、--p、p+5、p-=8
n 比较——利用比较运算符(==、!=、<、>、<=、>=)来比较两个迭代器是否相等或谁大谁小,例如if(p < q)……;、wihle(p != c.end())……;
////////迭代器是如何比较大小的?同一个迭代器是可以比较的
不同的泛型算法,对迭代器的要求也是不同的。例如,查找算法,只要求定义++操作符,以便迭代器能遍历整个容器,读取每一个元素的值来进行比较;但是,查找算法,并不需要修改数据,所以不要求写操作。排序算法则要求能随机访问,以便交换不相邻的元素;这需要对迭代器iter定义+操作,以便能够使用像iter + 12这样的表达式;另外,排序算法还要求可以读写数据。
(2)分类
根据迭代器所支持的操作不同,在STL中定义了如下5种迭代器:
l 输入迭代器(input iterator)——用于读取容器中的信息,但不一定能够修改它。
n 输入迭代器iter通过解除引用(即*iter),来读取容器中其所指向元素之值;
n 为了使输入迭代器能够访问容器中的所有元素的值,必须使其支持(前/后缀格式的)++ 操作符;
n 输入迭代器不能保证第二次遍历容器时,顺序不变;也不能保证其递增后,先前指向的值不变。即,基于输入迭代器的任何算法,都应该是单通(single-pass)的,不依赖于前一次遍历时的值,也不依赖于本次遍历中前面的值。
可见输入迭代器是一种单向的只读迭代器,可以递增但是不能递减,而且只能读不能写。适用于单通只读型算法。
l 输出迭代器(output iterator)——用于将信息传输给容器(修改容器中元素的值),但是不能读取。例如,显示器就是只能写不能读的设备,可用输出容器来表示它。也支持解除引用和++操作,也是单通的。所以,输出迭代器适用于单通只写型算法。
l 前向迭代器(forward iterator正向迭代器)——只能使用++操作符来单向遍历容器(不能用--)。与I/O迭代器一样,前向迭代器也支持解除引用与++操作。与I/O迭代器不同的是,前向迭代器是多通的(multi-pass)。即,它总是以同样的顺序来遍历容器,而且迭代器递增后,仍然可以通过解除保存的迭代器引用,来获得同样的值。另外,前向迭代器既可以是读写型的,也可以是只读的。
l 双向迭代器(bidirectional iterator)——可以用++和--操作符来双向遍历容器。其他与前向迭代器一样,也支持解除引用、也是多通的、也是可读写或只读的。
l 随机访问迭代器(random access iterator)——可直接访问容器中的任意一个元素的双向迭代器。
可见,这5种迭代器形成了一个层次结构:I/O迭代器(都可++遍历,但是前者只读/后者只写)最基本、前向迭代器可读写但只能++遍历、双向迭代器也可读写但能++/--双向遍历、随机迭代器除了能够双向遍历外还能够随机访问。
(5)指针与迭代器
既然迭代器是广义的指针,那么指针本身是不是迭代器呢?其实,指针满足所有迭代器的要求,所以,指针就是一种迭代器。
迭代器是泛型算法的接口,而指针是迭代器。所以,各种STL算法,也可以使用指针,来对非标准容器(如数组)进行操作。即,利用指针做迭代器,可以将STL算法用于常规数组。
例如排序函数sort:
sort(Ran first, Ran last); // Ran表示随机访问迭代器
对容器c为:
sort(c.begin(), c.end());
对数组a可以改为:(const int SIZE = 100; float a[SIZE];)
sort(a, a + SIZE);
又例如复制函数copy:
copy(In first, In last, Out res); // In和Out分别表示输入和输出迭代器
对容器c<int>可为:(ostream_iterator<int> out_iter(cout);)
copy(c.begin(), c.end(), out_iter);
对数组a可以改为:(const int SIZE = 100; float a[SIZE];)
copy(a, a + SIZE, c.begin());
容器 支持的迭代器类别 容器 支持的迭代器类别 容器 支持的迭代器类别
vector 随机访问 deque 随机访问 list 双向
set 双向 multiset 双向 map 双向
multimap 双向 stack 不支持 queue 不支持
priority_queue 不支持 hash_set 前向 hash_map 前向
#include<algorithmn>中的sort(Random Access Iterator begin,R--A--I iterater end,bool compare);支持随机访问的迭代器作为参数
迭代器的操作:
每种迭代器均可进行包括表中前一种迭代器可进行的操作。迭代器的操作本质上是通过重载运算符来实现的,迭代器支持何种操作和能够执行什么运算是由迭代器所重载的运算符来决定的。
迭代器类型 | 操作类型 | 说明 |
所有迭代器 |
p++ ++p |
后置自增迭代器 前置自增迭代器s's |
输入迭代器 |
*p p=p1 p==p1 p!=p1 |
复引用迭代器,作为右值 将一个迭代器赋给另一个迭代器 比较迭代器的相等性 比较迭代器的不等性 |
输出迭代器 |
*p p=p1 |
复引用迭代器,作为左值 将一个迭代器赋给另一个迭代器 |
正向迭代器 |
提供输入输出迭代器的所有功能 |
|
双向迭代器 |
--p p-- |
前置自减迭代器 后置自减迭代器 |
随机访问迭代器 |
p+=i p-=i p+i p-i p[i] p<p1 p<=p1 p>p1 p>=p1 |
将迭代器递增i位 将迭代器递减i位 在p位加i位后的迭代器 在p位减i位后的迭代器 返回p位元素偏离i位的元素引用 如果迭代器p的位置在p1前,返回true,否则返回false p的位置在p1的前面或同一位置时返回true,否则返回false 如果迭代器p的位置在p1后,返回true,否则返回false p的位置在p1的后面或同一位置时返回true,否则返回false |
功能访问上:随机访问迭代器》双向迭代器》正向迭代器》输出迭代器》输入迭代器
部分容器提供的的rbegin()和rend();的反向迭代器。没有写入进去,可以接下去总结:
3.1. STL容器
(1)序列式容器(Sequence containers),每个元素都有固定位置--取决于插入时机和地点,和元素值无关,vector、deque、list;
Vectors:将元素置于一个动态数组中加以管理,可以随机存取元素(用索引直接存取),数组尾部添加或移除元素非常快速。但是在中部或头部安插元素比较费时;
Deques:是“double-ended queue”的缩写,可以随机存取元素(用索引直接存取),数组头部和尾部添加或移除元素都非常快速。但是在中部或头部安插元素比较费时;
Lists:双向链表,不提供随机存取(按顺序走到需存取的元素,O(n)),在任何位置上执行插入或删除动作都非常迅速,内部只需调整一下指针;
(2)关联式容器(Associated containers),元素位置取决于特定的排序准则,和插入顺序无关,set、multiset、map、multimap;
Sets/Multisets:内部的元素依据其值自动排序,Set内的相同数值的元素只能出现一次,Multisets内可包含多个数值相同的元素,内部由二叉树实现,便于查找;
Maps/Multimaps:Map的元素是成对的键值/实值,内部的元素依据其值自动排序,Map内的相同数值的元素只能出现一次,Multimaps内可包含多个数值相同的元素,内部由二叉树实现,便于查找;
容器的比较:
Vector | Deque | List | Set | MultiSet | Map | MultiMap | |
内部结构 | dynamic array | array of arrays | double linked list | binary tree | binary tree | binary tree | binary tree |
随机存取 | Yes | Yes | No | No | No | Yes(key) | No |
搜索速度 | 慢 | 慢 | 很慢 | 快 | 快 | 快 | 快 |
快速插入移除 | 尾部 | 首尾 | 任何位置 | -- | -- | -- | -- |
迭代器的作用:能够让迭代器与算法不干扰的相互发展,最后又能无间隙的粘合起来,重载了*,++,==,!=,=运算符。用以操作复杂的数据结构,容器提供迭代器,算法使用迭代器;