C++学习笔记之迭代器

模板是的算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。理解迭代器是理解STL的关键。

迭代器应该具备的特征:

(1)应该能够对迭代器进行解除引用的操作,以便能够访问它引用的值。即如果P是一个迭代器,则应该对*P进行定义

(2)应该能够将一个迭代器赋给另一个迭代器。如果P和Q都是迭代器,则应对P=Q定义。

(3)应该能够对迭代器进行比较,看它们是否相等。即如果P和Q都是迭代器,则应对P==Q和P!=Q进行定义。

(4)应该能够使用迭代器遍历容器中的所有元素,这可以通过迭代器定义的++P和P++来实现。

值得注意的是,常规的指针就能够满足迭代器的要求,因此可以将指针看作是一种迭代器。

说了这么多,也许你并不理解,没关系,读完下面实例,再回头看这些陈述,你就会发现so easy!

下面来看具体实例解释:

首先在double数组中搜索特定值的函数,可以这样编写该函数:

1 double * find_ar(double * ar, int n, const double & val)
2 {
3     for (int i = 0; i < n; i++)
4         if (ar[i] == val)
5             return &ar[i];//成功找到,返回该值的地址
6     return 0; //否则返回空指针
7 }

可以看出,该算法是与特定的数据容器(数组)关联在一起的;

下面是搜索另一种数据结构-----链表的算法,每个节点的指针域都指向下一个节点,链表最后一个节点的指针被设为0:

同样,该算法是与特定的数据容器(数组)关联在一起的。

 1 struct Node
 2 {
 3     double item;
 4     Node * p_next;
 5 };
 6 Node * find_ll(Node * head, const double & val)
 7 {
 8     Node * start;
 9     for (start = head; start != 0; start = start->p_next)
10         if ( start->item == val)
11             return start;
12     return 0;
13 }

于是,大神们就想:有木有一种算法可以独立于这些数据容器(链表,数组等)呢?显然,操作相同(如:都是搜索一种元素),容器一变,算法就要重写,确实很麻烦。即搞出一种算法,不论数据容器怎么变,该算法都适用。于是迭代器应运而生!

重新编写find_arr()函数:

 1 typedef double * iterator;
 2 iterator find_ar(iterator begin, iterator end, const double & val)
 3 {
 4         iterator ar;
 5         for (ar = begin; ar != end; ar++)
 6         {
 7             if (*ar == val)
 8                 return ar;
 9             return end;//表示没有找到
10         }
11 }

程序解释:begin是指向数组起始位置,end指向数组的超尾,解释一下超尾, 超尾就是数组最后一个元素后面的一个元素(当然有可能是空值,但显然这个元素已经不在数组范围了)的位置,特别注意不要理解为数组最后一个元素的位置,这点很重要,搞清楚begin和end的概念之后,相信上面的程序很容易理解了。至于如何让begin指向数组第一个元素,如何让end指向超尾和本讨论无关,当然也丝毫不影响对本讨论的理解,所以完全不用多想这个问题。

对于find_ll()函数,先定义一个迭代器类,看不懂没关系,只需要感性上知道这个迭代器类定义了*,++,==,!=等运算符是干啥的。大致浏览一遍,定义iterator类不是重点(否则本讨论就应该改名“类的定义及其使用”,哈哈),千万不要纠结iterator类的定义,以为这种纠结会影响对本讨论的理解,再次声明本讨论主要介绍为什么使用迭代器,以及迭代器的原理,至于具体迭代器类的定义是根据具体需要可以自定义的,不用想太多,读完这篇,只要搞懂我们使用迭代器的意义在哪里就证明你完成了阅读任务,而具体的迭代器的定义,STL里面早就有人帮我们写好了,不是我们操心的事!这里写出来只是方便讨论,感性认识一下即可。

 1 struct Node
 2 {
 3     double item;
 4     Node * p_next;
 5 };
 6  class iterator
 7  {
 8  private:
 9      Node * pt;
10  public:
11      //构造函数重构,使用列表初始化
12      iterator() : pt(0) {}
13      iterator(Node * pn) : pt(pn) {}
14      double operator * () {return pt -> item;}//定义*操作符
15      iterator& operator++() //定义++前缀操作符,及++it
16      {
17          pt = pt->p_next;
18          return *this;
19      }
20      iterator& operator++(int) //定义++后缀操作符,及it++
21      {
22          iterator tmp = *this;
23          pt = pt->p_next;
24          return tmp;
25      }
26 //...operator==()operator!=(),etc
27  };

正如前面所说,重点不是如何定义iterator类,而是有了这样的类(库函数里有,大家不用就纠结如何定义哈,这样的类已经有了,否则我在这里废话半天有毛用),第二个搜索函数find_ll()可以改写为:

 1  iterator find_ll(iterator head, const double & val)
 2 {
 3         iterator start;
 4         for (start = head; start != 0; ++start)
 5         {
 6             if (*start == val)
 7                 return start;
 8             return 0;//表示没有找到
 9         }
10 }

可以发现这和find_ar()几乎相同,只是find_ar()使用超尾迭代词end,而find_ll()使用存储在最后一个节点中的空值0.

于是进一步改进,在定义数据容器的迭代器时,要求数组和链表都有一个超尾元素(C++在STL确实也是这样做的),并在迭代器达到超尾时结束搜索。这样,find_ar()和find_ll()检测数据尾的方式将相同,从而成为相同的算法。

最后做一个总结:STL遵循上面介绍的方法。首先,每个容器类(vector, list, deque)定义了相应的迭代器类型。对于其中某个类,迭代器可能是指针;而对于另一个容器类,则可能是对象。不管实现方式如何,迭代器都将提供所需要的操作,如*和++等等。其次,每一个容器类都一个超尾标记,当迭代器递增到超越容器的最后一个值后,这个值将被赋给迭代器。每个容器类都有begin()和end()方法,它们分别返回一个指向容器的第一个元素和超尾位置的迭代器。每个容器类都使用++操作,让迭代器从指向第一个元素逐步指向超尾位置,从而遍历容器中的每一个元素。

再次强调,使用容器类时,无需知道其迭代器是如何实现的,也无需知道超尾是如何实现的,而只需知道它有迭代器,其begin()返回一个指向第一个元素的迭代器,end()一个指向超尾位置的迭代器即可。

C++学习笔记之迭代器,布布扣,bubuko.com

时间: 2024-11-03 05:40:10

C++学习笔记之迭代器的相关文章

STL学习笔记(迭代器相关辅助函数)

advance()可令迭代器前进 #include <iterator> void advance(InputIterator& pos,Dist n); 面对Random Access(随机存取)迭代器,该函数只是简单的调用pos+=n.而对于其他任何类型的迭代器则调用++pos(--pos)n次. distance()可以处理迭代器之间的距离 #include <iterator> Dist distance(InputIterator pos1,InputIterat

python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化

生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: 1 >>> g = (x * x for xin range(10)) 2 >>> g3 <generator object <genexpr> at 0x1022ef630> 此处g就是一个生成器. 迭代器 我们已经知道,可以直接作用于for循环的数据类型有以下几种: 一类是集合数据类型,如list.tuple.dict.set.str等: 一类是gene

STL学习笔记(迭代器类型)

迭代器类型 迭代器是一种“能够遍历某个序列内的所有元素”的对象.它可以透过与一般指针一致的接口来完成自己的工作. 不同的迭代器具有不同的”能力“(行进和存取能力) Input迭代器 Input迭代器只能一次一个向前读取元素,按此顺序一个个传回元素值. 几乎所有迭代器都具备Input迭代器的能力,而且通常更强.纯粹Input迭代器的一个典型例子就是“从标准输入装置读取数据”的迭代器. 下表列出了Input迭代器的各种操作行为 Output迭代器 Output迭代器和Input迭代器相反,其作用是将

STL学习笔记(迭代器配接器)

Reverse(逆向)迭代器 Reverse迭代器是一种配接器. 重新定义递增运算和递减运算.使其行为正好倒置. 如果你使用这类迭代器,算法将以逆向次序处理元素.所有标准容器都允许使用Reverse迭代器来遍历元素.下面是个例子: 1 #include <iostream> 2 #include <list> 3 #include <algorithm> 4 using namespace std; 5 6 void print(int elem) 7 { 8 cout

Python学习笔记010_迭代器

迭代就类似于循环,每次重复的过程被称为迭代的过程,每次迭代的结果将被用来作为下一次迭代的初始值,提供迭代方法的容器被称为迭代器. 常见的迭代器有 (列表.元祖.字典.字符串.文件 等),通常我们是使用for语句完成迭代 #使用for 迭代字典的例子:>>> links = {"鱼C工作室":"http://www.fishc.com/", "鱼C论坛":"http://bbc.fishc.com"} >

STL学习笔记— —iterator迭代器

头文件 所有的容器都定义了自己的迭代器类型,所以一般情况下,不需要再自己添加迭代器头文件.但是如果要使用一些特殊的迭代器,如反向迭代器,或者一些迭代器辅助函数,那么就需要添加头文件<iterator>. 迭代器类型 类型 能力 提供者 输入迭代器 一次向前读取 istream 输出迭代器 向前写入 ostream,inserter 前向迭代器 向前读取和写入 forward list,unordered containers 双向迭代器 向前和向后的读取和写入 list,set,multise

Lua学习笔记之迭代器与范型for

1.  迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是它知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是以恶搞内部函数,它可以访问一个或者多个外部函数的外部局部变量.每一次闭包的成功调用后这些局部变量都保存他们的值. 2.  范性for的语义 范性for的文法如下: for <var-list>in <exp-list> do <body> end

Lua学习笔记(七):迭代器与泛型for

1.迭代器与闭包 迭代器是一种支持指针类型的结构,它可以遍历集合的每一个元素.在Lua中我们常常使用函数来描述迭代器,每次调用该函数就返回集合的下一个元素. 迭代器需要保留上一次成功调用的状态和下一次成功调用的状态,也就是他知道来自于哪里和将要前往哪里.闭包提供的机制可以很容易实现这个任务.记住:闭包是一个内部函数,它可以访问一个或者多个外部函数的外部局部变量.每次闭包的成功调用后这些外部局部变量都保存他们的值(状态).当然如果要创建一个闭包必须要创建其外部局部变量.所以一个典型的闭包的结构包含

opencv学习笔记(03)——遍历图像(迭代器法)

1 #include <opencv2\highgui\highgui.hpp> 2 #include <opencv2\imgproc\imgproc.hpp> 3 #include <opencv2\core\core.hpp> 4 5 void colorReduce(cv::Mat& img, int div=64); 6 7 8 int main() 9 { 10 cv::Mat img_orginal = cv::imread("F:\\i