在 STL 中,迭代器(Iterator)用来访问和检查 STL 容器中元素的对象,它的行为模式和指针类似,但是它封装了一些有效性检查,并且提供了统一的访问格式。
迭代器的设计思维是STL的关键所在,STL的中心思想在于将容器(container)和算法(algorithms)分开,彼此独立设计,最后再一贴胶着剂将他们撮合在一起。
迭代器(iterator)是一种抽象的设计概念,现实程序语言中并没有直接对应于这个概念的实物。 在<
>一书中提供了23种设计模式的完整描述, 其中iterator模式定义如下:提供一种方法,使之能够依序寻访某个容器所含的各个元素,而又无需暴露该容器的内部表示方式。 从技术角度来看,容器和算法的泛型化并不困难,c++的class template和function template可分别达到目标,如果设计出两这个之间的良好的胶着剂,才是大难题。
1. 基本使用
概念虽然晦涩难懂,但是在使用时可以当作指针,大部分迭代器都支持这两个运算符:自增 运算符( ++ )和解引用运算符(* ),其中自增用来移动迭代器,解引用可以获取或修改它指向的元素。
2. 迭代器分类
迭代器总共又五种,分别是:输入迭代器,输出迭代器,正向迭代器,双向迭代器和随机访问迭代器。具体区别见下表:
| 迭代器 | 描述 | 支持的运算符 | 读写 |
|---|---|---|---|
| 输入迭代器 | 提供对数据的只读访问 | ++、*(右值)、=、==、!=、-> | 只读 |
| 输出迭代器 | 提供对数据的只写访问 | ++、*(左值)、= | 只写 |
| 正向迭代器 | 提供读写操作,并能向前推进迭代器 | ++、*、=、==、!=、-> | 读写 |
| 双向迭代器 | 提供读写操作,并能向前和向后操作 | ++、*、=、==、!=、->、— | 读写 |
| 随机访问迭代器 | 提供读写操作,并能以跳跃的方式访问容器的任意数据,是功能最强的迭代器 | ++、*、=、==、!=、->、—、+=、-=、+、-、[]、<、<=、>、>= | 读写 |
2.1. 输入迭代器(input iterator)
可以读取序列中的元素。一个输入迭代器必须支持:
- 用于比较两个迭代器的相等和不相等运算符(
==、!=) - 用于推进迭代器的前置和后置递增运算(
++) - 用于读取元素的解引用运算符(
*);解引用只会出现在赋值运算符的右侧 - 箭头运算符(
->),等价于(*it).member,即,解引用迭代器,并提取对象的成员
输入迭代器只用于顺序访问。对于一个输入迭代器,*it++保证是有效的,但递增它可能导致所有其他指向流的迭代器失效。其结果就是,不能保证输入迭代器的状态可以保存下来并用来访问元素。因此,输入迭代器只能用于单遍扫描算法。算法find和accumulate要求输入迭代器;而istream_iterator是一种输入迭代器。
2.2.输出迭代器(output iterator)
可以看作输入迭代器功能上的补集——只写而不读元素。输出迭代器必须支持:
- 用于推进迭代器的前置和后置递增运算(
++) - 解引用运算符(
*),只出现在赋值运算符的左侧(向一个已经解引用的输出迭代器赋值,就是将值写入它所指向的元素)
我们只能向一个输出迭代器赋值一次。类似输入迭代器,输出迭代器只能用于单遍扫面算法。用作目的位置的迭代器通常都是输出迭代器。例如,copy函数的第三个参数就是输出迭代器。ostream_iterator类型也是输出迭代器。
2.3. 前向迭代器(forward iterator)
可以读写元素。这类迭代器只能在序列中沿一个方向移动。前向迭代器支持所有输入和输出迭代器的操作,而且可以多次读写同一个元素。因此,我们可以保存前向迭代器的状态,使用前向迭代器的算法可以对序列进行多遍扫描。算法replace要求前向迭代器,forward_list上的迭代器是前向迭代器。
演示:支持通过 ++ 自增(前移),通过 * 访问或赋值。
#include <iostream>#include <forward_list>using namespace std;int main() {// forward_list 是 STL 中的单向链表,可以获得前向迭代器forward_list<int> l{1, 2, 3};forward_list<int>::iterator iter = l.begin();for (; iter != l.end(); ++iter) {cout << *iter << ' '; // 输出: 1 2 3}return 0;}
2.4. 双向迭代器(bidirectional iterator)
可以正向/反向读写序列中的元素。除了支持所有前向迭代器的操作之外,双向迭代器还支持前置和后置递减运算符(--)。算法reverse要求双向迭代器,除了forward_list之外,其他标准库都提供符合双向迭代器要求的迭代器。
演示:在前向迭代器的基础上,增加了自减(--)的支持。
#include <iostream>#include <list>using namespace std;int main() {// list 是 STL 中的双向链表,可以获得双向迭代器list<int> l{1, 2, 3};list<int>::iterator iter = l.begin();for (; iter != l.end(); ++iter) {cout << *iter << ' '; // 输出: 1 2 3}cout << endl;while (iter != l.begin()) {--iter;cout << *iter << ' '; // 输出: 3 2 1}return 0;}
2.5. 随机访问迭代器(random-access iterator)
提供在常量时间内访问序列中任意元素的能力。此类迭代器支持双向迭代器的所有功能,此外还支持如下操作:
- 用于比较两个迭代器相对位置的关系运算符(
<,<=,>和>=) - 迭代器和一个整数值的加减运算(
+,+=,-,-=),计算结果是迭代器在序列中前进(或后退)给定整数个元素后的位置 - 用于两个迭代器上的减法运算符(
-),得到两个迭代器的距离 - 下标运算符(
iter[n]),与*(iter[n])等价
演示:在双向迭代器的基础上,增加了加减运算和比较运算的支持。
#include <iostream>#include <vector>using namespace std;int main() {// vector 是 STL 对数组的封装vector<int> v{1, 2, 3, 4, 5};for (vector<int>::iterator iter = v.begin(); iter < v.end(); iter += 2) {cout << *iter << ' '; // 输出: 1 3 5}return 0;}
引用自【C/C++】STL详解——迭代器应用演示,作者:沉晓
#define _CRT_SECURE_NO_WARNINGS#include<iostream>#include<vector>#include<algorithm>using namespace std;//STL 中的容器 算法 迭代器void test01(){vector<int> v; //STL 中的标准容器之一 :动态数组v.push_back(1); //vector 容器提供的插入数据的方法v.push_back(5);v.push_back(3);v.push_back(7);//迭代器vector<int>::iterator pStart = v.begin(); //vector 容器提供了 begin()方法 返回指向第一个元素的迭代器vector<int>::iterator pEnd = v.end(); //vector 容器提供了 end()方法 返回指向最后一个元素下一个位置的迭代器//通过迭代器遍历while (pStart != pEnd){cout << *pStart << " ";pStart++;}cout << endl;//算法 count 算法 用于统计元素的个数int n = count(pStart, pEnd, 5);cout << "n:" << n << endl;}//STL 容器不单单可以存储基础数据类型,也可以存储类对象class Teacher{public:Teacher(int age) :age(age){};~Teacher(){};public:int age;};void test02(){vector<Teacher> v; //存储 Teacher 类型数据的容器Teacher t1(10), t2(20), t3(30);v.push_back(t1);v.push_back(t2);v.push_back(t3);vector<Teacher>::iterator pStart = v.begin();vector<Teacher>::iterator pEnd = v.end();//通过迭代器遍历while (pStart != pEnd){cout << pStart->age << " ";pStart++;}cout << endl;}//存储 Teacher 类型指针void test03(){vector<Teacher*> v; //存储 Teacher 类型指针Teacher* t1 = new Teacher(10);Teacher* t2 = new Teacher(20);Teacher* t3 = new Teacher(30);v.push_back(t1);v.push_back(t2);v.push_back(t3);//拿到容器迭代器vector<Teacher*>::iterator pStart = v.begin();vector<Teacher*>::iterator pEnd = v.end();//通过迭代器遍历while (pStart != pEnd){cout << (*pStart)->age << " ";pStart++;}cout << endl;}//容器嵌套容器 难点void test04(){vector< vector<int> > v;vector<int>v1;vector<int>v2;vector<int>v3;for (int i = 0; i < 5;i++){v1.push_back(i);v2.push_back(i * 10);v3.push_back(i * 100);}v.push_back(v1);v.push_back(v2);v.push_back(v3);for (vector< vector<int> >::iterator it = v.begin(); it != v.end();it++){for (vector<int>::iterator subIt = (*it).begin(); subIt != (*it).end(); subIt ++){cout << *subIt << " ";}cout << endl;}}int main(){//test01();//test02();//test03();test04();system("pause");return EXIT_SUCCESS;}
