在 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上的迭代器是前向迭代器。

演示:支持通过 ++ 自增(前移),通过 * 访问或赋值。

  1. #include <iostream>
  2. #include <forward_list>
  3. using namespace std;
  4. int main() {
  5. // forward_list 是 STL 中的单向链表,可以获得前向迭代器
  6. forward_list<int> l{1, 2, 3};
  7. forward_list<int>::iterator iter = l.begin();
  8. for (; iter != l.end(); ++iter) {
  9. cout << *iter << ' '; // 输出: 1 2 3
  10. }
  11. return 0;
  12. }

2.4. 双向迭代器(bidirectional iterator)

可以正向/反向读写序列中的元素。除了支持所有前向迭代器的操作之外,双向迭代器还支持前置和后置递减运算符(--)。算法reverse要求双向迭代器,除了forward_list之外,其他标准库都提供符合双向迭代器要求的迭代器。
演示:在前向迭代器的基础上,增加了自减(--)的支持。

  1. #include <iostream>
  2. #include <list>
  3. using namespace std;
  4. int main() {
  5. // list 是 STL 中的双向链表,可以获得双向迭代器
  6. list<int> l{1, 2, 3};
  7. list<int>::iterator iter = l.begin();
  8. for (; iter != l.end(); ++iter) {
  9. cout << *iter << ' '; // 输出: 1 2 3
  10. }
  11. cout << endl;
  12. while (iter != l.begin()) {
  13. --iter;
  14. cout << *iter << ' '; // 输出: 3 2 1
  15. }
  16. return 0;
  17. }

2.5. 随机访问迭代器(random-access iterator)

提供在常量时间内访问序列中任意元素的能力。此类迭代器支持双向迭代器的所有功能,此外还支持如下操作:

  • 用于比较两个迭代器相对位置的关系运算符(<,<=,>>=
  • 迭代器和一个整数值的加减运算(+,+=,-,-=),计算结果是迭代器在序列中前进(或后退)给定整数个元素后的位置
  • 用于两个迭代器上的减法运算符(-),得到两个迭代器的距离
  • 下标运算符(iter[n]),与 *(iter[n]) 等价

演示:在双向迭代器的基础上,增加了加减运算和比较运算的支持。

  1. #include <iostream>
  2. #include <vector>
  3. using namespace std;
  4. int main() {
  5. // vector 是 STL 对数组的封装
  6. vector<int> v{1, 2, 3, 4, 5};
  7. for (vector<int>::iterator iter = v.begin(); iter < v.end(); iter += 2) {
  8. cout << *iter << ' '; // 输出: 1 3 5
  9. }
  10. return 0;
  11. }

引用自【C/C++】STL详解——迭代器应用演示,作者:沉晓

  1. #define _CRT_SECURE_NO_WARNINGS
  2. #include<iostream>
  3. #include<vector>
  4. #include<algorithm>
  5. using namespace std;
  6. //STL 中的容器 算法 迭代器
  7. void test01(){
  8. vector<int> v; //STL 中的标准容器之一 :动态数组
  9. v.push_back(1); //vector 容器提供的插入数据的方法
  10. v.push_back(5);
  11. v.push_back(3);
  12. v.push_back(7);
  13. //迭代器
  14. vector<int>::iterator pStart = v.begin(); //vector 容器提供了 begin()方法 返回指向第一个元素的迭代器
  15. vector<int>::iterator pEnd = v.end(); //vector 容器提供了 end()方法 返回指向最后一个元素下一个位置的迭代器
  16. //通过迭代器遍历
  17. while (pStart != pEnd){
  18. cout << *pStart << " ";
  19. pStart++;
  20. }
  21. cout << endl;
  22. //算法 count 算法 用于统计元素的个数
  23. int n = count(pStart, pEnd, 5);
  24. cout << "n:" << n << endl;
  25. }
  26. //STL 容器不单单可以存储基础数据类型,也可以存储类对象
  27. class Teacher
  28. {
  29. public:
  30. Teacher(int age) :age(age){};
  31. ~Teacher(){};
  32. public:
  33. int age;
  34. };
  35. void test02(){
  36. vector<Teacher> v; //存储 Teacher 类型数据的容器
  37. Teacher t1(10), t2(20), t3(30);
  38. v.push_back(t1);
  39. v.push_back(t2);
  40. v.push_back(t3);
  41. vector<Teacher>::iterator pStart = v.begin();
  42. vector<Teacher>::iterator pEnd = v.end();
  43. //通过迭代器遍历
  44. while (pStart != pEnd){
  45. cout << pStart->age << " ";
  46. pStart++;
  47. }
  48. cout << endl;
  49. }
  50. //存储 Teacher 类型指针
  51. void test03(){
  52. vector<Teacher*> v; //存储 Teacher 类型指针
  53. Teacher* t1 = new Teacher(10);
  54. Teacher* t2 = new Teacher(20);
  55. Teacher* t3 = new Teacher(30);
  56. v.push_back(t1);
  57. v.push_back(t2);
  58. v.push_back(t3);
  59. //拿到容器迭代器
  60. vector<Teacher*>::iterator pStart = v.begin();
  61. vector<Teacher*>::iterator pEnd = v.end();
  62. //通过迭代器遍历
  63. while (pStart != pEnd){
  64. cout << (*pStart)->age << " ";
  65. pStart++;
  66. }
  67. cout << endl;
  68. }
  69. //容器嵌套容器 难点
  70. void test04()
  71. {
  72. vector< vector<int> > v;
  73. vector<int>v1;
  74. vector<int>v2;
  75. vector<int>v3;
  76. for (int i = 0; i < 5;i++)
  77. {
  78. v1.push_back(i);
  79. v2.push_back(i * 10);
  80. v3.push_back(i * 100);
  81. }
  82. v.push_back(v1);
  83. v.push_back(v2);
  84. v.push_back(v3);
  85. for (vector< vector<int> >::iterator it = v.begin(); it != v.end();it++)
  86. {
  87. for (vector<int>::iterator subIt = (*it).begin(); subIt != (*it).end(); subIt ++)
  88. {
  89. cout << *subIt << " ";
  90. }
  91. cout << endl;
  92. }
  93. }
  94. int main(){
  95. //test01();
  96. //test02();
  97. //test03();
  98. test04();
  99. system("pause");
  100. return EXIT_SUCCESS;
  101. }