一. 前言
C++ 标准库提供了许多容器,如 list,vector,deque 等,同时标准库还提供了一种方便程序员遍历访问 STL 的手段,例如我们可以使用下标的方式来访问 vector 容器,但是对于 list 容器,显然用下标来遍历访问不太合理。
vector<int> vec = {1,2,3,4,5,6,7};
for(decltype(vec.size()) i = 0;i < vec.size();++i)
{
cout << vec[i] << " ";
}
如果使用迭代器的方式遍历 vector,可以改成如下的方式。
vector<int> vec = {1,2,3,4,5,6,7};
for(auto it = vec.begin();it != vec.end();++it)
{
cout << *it << " ";
}
如果你想访问 list 容器,也可以使用类似于上面的方式访问。
二. 迭代器的概念
迭代器在行为上是一种类似于指针的对象,它表示容器中的某一个位置,迭代器紧密依赖于容器,是由容器提供的,我们可以通过解引用的方式获得容器中该位置的元素值。
三. 迭代器的分类
迭代器可以分为输入迭代器,输出迭代器,前向迭代器,双向迭代器和随机访问迭代器。迭代器分类的依据是:迭代器的移动特性以及在这个迭代器上所能做的操作。对于 vector 容器,其迭代器如果想往前或者往后移动到若干个元素前后的位置,只需要对迭代器加减要移动的元素个数值就可以了,对于 list,由于容器本身不是线性存储的,因此迭代器的移动就不能加上一个数字来移动了,只能通过 ++ 和 -- 移动 list 迭代器,所以我们称 vector<type>::iterator 是随机访问迭代器,list<type>::iterator 是双向迭代器。
(一)输入迭代器
表达式 | 含义 |
iter1 == iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不相等 |
++iter | 迭代器向前移动,前置版本 |
iter++ | 迭代器向前移动,后置版本 |
*iter | 读取迭代器指向的容器元素 |
iter->member | 等价于(*it).member |
TYPE(iter) | 复制迭代器 |
一个输入迭代器必须支持比较两个迭代器是否相等;用于推进迭代器位置向前移动的前置和后置递增运算,注意我们这里所说的向前移动是指从 begin-> end 这个方向;用于读取元素的解引用运算符,并且解引用只能出现在赋值运算符右侧;箭头运算符,等价于(*it).member。输入迭代器只能用于单遍扫描算法,例如算法 find 和 accumulate 要求的迭代器类型就是输入迭代器类型。
istream_iterator 就是一种输入迭代器。
(二)输出迭代器
表达式 | 含义 |
*iter = val | 将val写入迭代器所指的位置 |
++iter | 迭代器向前移动,前置版本 |
iter++ | 迭代器向前移动,后置版本 |
TYPE(iter) | 复制迭代器 |
输出迭代器可以看做是输入迭代器的补集,只写而不读元素。输出迭代器需要支持推进迭代器位置向前移动的前置和后置版本的递增运算;解引用运算符,并且只出现在赋值运算符的左侧,我们只能向一个输出迭代器赋值一次。
ostream_iterator 和 inserter 就是输出迭代器。
(三)前向迭代器
表达式 | 含义 |
*iter | 读取迭代器指向的容器元素 |
iter->member | 等价于(*it).member |
++iter | 迭代器向前移动,前置版本 |
iter++ | 迭代器向前移动,后置版本 |
iter1 == iter2 | 判断两个迭代器是否相等 |
iter1 != iter2 | 判断两个迭代器是否不相等 |
TYPE() | 创建迭代器 |
TYPE(iter) | 复制迭代器 |
iter1 = iter2 | 使用迭代器iter2给迭代器iter1赋值 |
前向迭代器可以读写元素,这类迭代器只能在序列中沿一个方向移动,前向迭代器支持所有输入和输出迭代器的操作,而且可以多次读写同一个元素。
forward_list 容器中定义的的迭代器就是前向迭代器。
(四)双向迭代器
表达式 | 含义 |
--iter | 迭代器往后移动,前置版本 |
iter-- | 迭代器往后移动,后置版本 |
双向迭代器可以正反方向读写序列中的元素,除了支持前向迭代器的所有操作以外,还提供了递减运算符,代表让迭代器往后移动。
list,set,multiset,map 和 multimap 容器中定义的迭代器属于双向迭代器类型。
(五)随机访问迭代器
表达式 | 含义 |
iter[n] | 访问索引位置为n的元素 |
iter+=n | 向前移动n个元素的位置 |
iter-=n | 向后回退n个元素的位置 |
iter+n | 返回iter后第n个元素的位置 |
iter-n | 返回iter前第n个元素的位置 |
iter1-iter2 | 返回iter1和iter2之间的距离 |
iter1<iter2 | 判断iter1是否在iter2之前 |
iter1>iter2 | 判断iter1是否在iter2之后 |
iter1<=iter2 | 判断iter1是否不在iter2之后 |
iter1>=iter2 | 判断iter1是否不在iter2之前 |
随机访问迭代器提供在常量时间内访问序列中任意元素的能力,此类迭代器不仅支持双向迭代器的所有功能,还提供用于比较两个迭代器相对位置的关系运算符(<,<=,>,>=);迭代器和一个整数值的加减运算,计算结果是迭代器在序列中前进或者后退给定整数个元素后的位置;两个迭代器间的减法运算,得到两个迭代器的距离;下标运算符。
array,vector,deque,string 容器定义的迭代器就是随机访问迭代器类型。
四. 使用程序判断迭代器的类型
那么怎么通过程序来验证容器迭代器的类型呢?只要使用萃取技术即可。
#include <iostream>
#include <array>
#include <vector>
#include <map>
#include <list>
#include <iterator>
using namespace std;
void _display_category(input_iterator_tag)
{
cout << "input_iterator_tag" << endl;
}
void _display_category(forward_iterator_tag)
{
cout << "forward_iterator_tag" << endl;
}
void _display_category(bidirectional_iterator_tag)
{
cout << "bidirectional_iterator_tag" << endl;
}
void _display_category(random_access_iterator_tag)
{
cout << "random_access_iterator_tag" << endl;
}
template <typename Type>
void display_category(Type type)
{
cout << "--------------begin--------------" << endl;
typename iterator_traits<Type>::iterator_category cateGory;
_display_category(cateGory);
cout << "" << typeid(type).name() << endl;
cout << "--------------end--------------" << endl;
}
int main()
{
display_category(array<int, 10>::iterator());
display_category(list<int>::iterator());
display_category(map<int, int>::iterator());
display_category(vector<int>::iterator());
return 0;
}
使用 list<int>::iterator() 或者 map<int,int>::iterator() 都可以生成一个类型对象,传递进模板的 type 进去,再使用萃取技术就可以获取类型。