C++学习笔记之迭代器
模板是的算法独立于存储的数据类型,而迭代器使算法独立于使用的容器类型。理解迭代器是理解STL的关键。
迭代器应该具备的特征:
(1)应该能够对迭代器进行解除引用的操作,以便能够访问它引用的值。即如果P是一个迭代器,则应该对*P进行定义
(2)应该能够将一个迭代器赋给另一个迭代器。如果P和Q都是迭代器,则应对P=Q定义。
(3)应该能够对迭代器进行比较,看它们是否相等。即如果P和Q都是迭代器,则应对P==Q和P!=Q进行定义。
(4)应该能够使用迭代器遍历容器中的所有元素,这可以通过迭代器定义的++P和P++来实现。
值得注意的是,常规的指针就能够满足迭代器的要求,因此可以将指针看作是一种迭代器。
说了这么多,也许你并不理解,没关系,读完下面实例,再回头看这些陈述,你就会发现so easy!
下面来看具体实例解释:
首先在double数组中搜索特定值的函数,可以这样编写该函数:
double * find_ar(double * ar, int n, const double & val)
{
for (int i = ; i < n; i++)
if (ar[i] == val)
return &ar[i];//成功找到,返回该值的地址
return ; //否则返回空指针
}
可以看出,该算法是与特定的数据容器(数组)关联在一起的;
下面是搜索另一种数据结构-----链表的算法,每个节点的指针域都指向下一个节点,链表最后一个节点的指针被设为0:
同样,该算法是与特定的数据容器(数组)关联在一起的。
struct Node
{
double item;
Node * p_next;
};
Node * find_ll(Node * head, const double & val)
{
Node * start;
for (start = head; start != ; start = start->p_next)
if ( start->item == val)
return start;
return ;
}
于是,大神们就想:有木有一种算法可以独立于这些数据容器(链表,数组等)呢?显然,操作相同(如:都是搜索一种元素),容器一变,算法就要重写,确实很麻烦。即搞出一种算法,不论数据容器怎么变,该算法都适用。于是迭代器应运而生!
重新编写find_arr()函数:
typedef double * iterator;
iterator find_ar(iterator begin, iterator end, const double & val)
{
iterator ar;
for (ar = begin; ar != end; ar++)
{
if (*ar == val)
return ar;
return end;//表示没有找到
}
}
程序解释:begin是指向数组起始位置,end指向数组的超尾,解释一下超尾, 超尾就是数组最后一个元素后面的一个元素(当然有可能是空值,但显然这个元素已经不在数组范围了)的位置,特别注意不要理解为数组最后一个元素的位置,这点很重要,搞清楚begin和end的概念之后,相信上面的程序很容易理解了。至于如何让begin指向数组第一个元素,如何让end指向超尾和本讨论无关,当然也丝毫不影响对本讨论的理解,所以完全不用多想这个问题。
对于find_ll()函数,先定义一个迭代器类,看不懂没关系,只需要感性上知道这个迭代器类定义了*,++,==,!=等运算符是干啥的。大致浏览一遍,定义iterator类不是重点(否则本讨论就应该改名“类的定义及其使用”,哈哈),千万不要纠结iterator类的定义,以为这种纠结会影响对本讨论的理解,再次声明本讨论主要介绍为什么使用迭代器,以及迭代器的原理,至于具体迭代器类的定义是根据具体需要可以自定义的,不用想太多,读完这篇,只要搞懂我们使用迭代器的意义在哪里就证明你完成了阅读任务,而具体的迭代器的定义,STL里面早就有人帮我们写好了,不是我们操心的事!这里写出来只是方便讨论,感性认识一下即可。
struct Node
{
double item;
Node * p_next;
};
class iterator
{
private:
Node * pt;
public:
//构造函数重构,使用列表初始化
iterator() : pt() {}
iterator(Node * pn) : pt(pn) {}
double operator * () {return pt -> item;}//定义*操作符
iterator& operator++() //定义++前缀操作符,及++it
{
pt = pt->p_next;
return *this;
}
iterator& operator++(int) //定义++后缀操作符,及it++
{
iterator tmp = *this;
pt = pt->p_next;
return tmp;
}
//...operator==()operator!=(),etc
};
正如前面所说,重点不是如何定义iterator类,而是有了这样的类(库函数里有,大家不用就纠结如何定义哈,这样的类已经有了,否则我在这里废话半天有毛用),第二个搜索函数find_ll()可以改写为:
iterator find_ll(iterator head, const double & val)
{
iterator start;
for (start = head; start != ; ++start)
{
if (*start == val)
return start;
return ;//表示没有找到
}
}
可以发现这和find_ar()几乎相同,只是find_ar()使用超尾迭代词end,而find_ll()使用存储在最后一个节点中的空值0.
于是进一步改进,在定义数据容器的迭代器时,要求数组和链表都有一个超尾元素(C++在STL确实也是这样做的),并在迭代器达到超尾时结束搜索。这样,find_ar()和find_ll()检测数据尾的方式将相同,从而成为相同的算法。
最后做一个总结:STL遵循上面介绍的方法。首先,每个容器类(vector, list, deque)定义了相应的迭代器类型。对于其中某个类,迭代器可能是指针;而对于另一个容器类,则可能是对象。不管实现方式如何,迭代器都将提供所需要的操作,如*和++等等。其次,每一个容器类都一个超尾标记,当迭代器递增到超越容器的最后一个值后,这个值将被赋给迭代器。每个容器类都有begin()和end()方法,它们分别返回一个指向容器的第一个元素和超尾位置的迭代器。每个容器类都使用++操作,让迭代器从指向第一个元素逐步指向超尾位置,从而遍历容器中的每一个元素。
再次强调,使用容器类时,无需知道其迭代器是如何实现的,也无需知道超尾是如何实现的,而只需知道它有迭代器,其begin()返回一个指向第一个元素的迭代器,end()一个指向超尾位置的迭代器即可。
C++学习笔记之迭代器的更多相关文章
- python学习笔记之迭代器和函数(第三天)
一.collection系列: 1.counter计数器 如果counter(dict)是对字典的一个补充,如果counter(list)则是对列表的补充,初步测试对字典的值进行排序. ####### ...
- python学习笔记(5)--迭代器,生成器,装饰器,常用模块,序列化
生成器 在Python中,一边循环一边计算的机制,称为生成器:generator. 如: >>> g = (x * x for xin range(10)) >>> ...
- python学习笔记四 迭代器,生成器,装饰器(基础篇)
迭代器 __iter__方法返回一个迭代器,它是具有__next__方法的对象.在调用__next__方法时,迭代器会返回它的下一个值,若__next__方法调用迭代器 没有值返回,就会引发一个Sto ...
- Python学习笔记010_迭代器_生成器
迭代器 迭代就类似于循环,每次重复的过程被称为迭代的过程,每次迭代的结果将被用来作为下一次迭代的初始值,提供迭代方法的容器被称为迭代器. 常见的迭代器有 (列表.元祖.字典.字符串.文件 等),通常 ...
- 学习笔记: yield迭代器
yield 与 IEnumerable<T> 结对出现, 可实现按需获取 , 迭代器模式 static void Main(string[] args) { ...
- python3学习笔记10(迭代器和生成器)
参考http://www.runoob.com/python3/python3-iterator-generator.html 迭代器 迭代器对象从集合的第一个元素开始访问,直到所有的元素被访问完结束 ...
- Python学习笔记014——迭代器 Iterator
1 迭代器的定义 凡是能被next()函数调用并不断返回一个值的对象均称之为迭代器(Iterator) 2 迭代器的说明 Python中的Iterator对象表示的是一个数据流,被函数next()函数 ...
- STL学习笔记(迭代器配接器)
Reverse(逆向)迭代器 Reverse迭代器是一种配接器. 重新定义递增运算和递减运算.使其行为正好倒置. 如果你使用这类迭代器,算法将以逆向次序处理元素.所有标准容器都允许使用Reverse迭 ...
- STL学习笔记(迭代器相关辅助函数)
advance()可令迭代器前进 #include <iterator> void advance(InputIterator& pos,Dist n); 面对Random Acc ...
随机推荐
- 【LR】安装LR11后遇到的问题
(1)问题:录制脚本时无法弹出IE浏览器 解决方法: 正确的是C:\Program Files (x86)\Internet Explorer\iexplore.exe 错误是:C:\Program ...
- Jersey搭建Restful服务器 on Ubuntu
自己试着在Ubuntu上搭建一个 1.首先安装eclipse和tomcat sudo apt-get install eclipse tomcat7 -y 2.把tomcat的group assign ...
- C# 异步操作 async await 的用法
1. async与 await 成对出现 async 在方法前使用 ,方法体面面用 await . 2. 使用async 和await定义异步方法不会创建新线程. 3.await 后面一定是一个扫行 ...
- Android百度地图开发(五)公交线路详情搜索、多条线路显示
一.公交线路详情检索 获取公交线路的详情主要分来两步,1.获取公交线路的Uid,2.通过Uid获取公交线路详情. 1.获取公交线路的Uid: /* * 获得公交线路图的Uid,并且根据系Uid发起公交 ...
- MySQL_PHP学习笔记_2015_0614_PHP传参总结_URL传参_表单传参
1. PHP 传参总结 1.1 url 传参 解析方法(下面两种解读方式均可以): $firstName1 = $_GET['firstName']; $firstName2 = $_RE ...
- linux 学习网站
study-Area:http://www.study-area.org 鸟哥的私房菜:http://linux.vbird.org 鸟哥的私房菜课后答案:http://wapwenku.baidu. ...
- 从cocos2dx中寻找函数指针传递的方法
目的 看到群里有个朋友搞了好几天函数指针传递,没搞好.所以写一篇文章,旨在从cocos2dx中帮朋友们找到如何传递指针. 旧版本的函数指针传递 全局函数函数指针调用 一般在C++11之前,我们一般是这 ...
- Page 16 Exercises 1.2.3 -------Introduction to Software Testing (Paul Ammann and Jeff Offutt)
Below are four faulty programs. Each includes a test case that results in failure. Answer the follow ...
- cJSON学习笔记
1.JSON格式简述 JSON(JavaScript Object Notation) 是一种轻量级的数据交换格式.易于人阅读和编写,同时也易于机器解析和生成.它基于JavaScript(Standa ...
- Android APP的安装路径
转载自:http://blog.csdn.net/libaineu2004/article/details/25247711 一.安装路径在哪? Android应用安装涉及到如下几个目录: syste ...