定义C++迭代器

所有的STL容器都定义了:

 

没有定义上面两种的容器被看做二等公民,他们不能和泛型算法一起使用,为你的容器定义迭代器类型和begin、end方法,无论他们是否是泛型容器

 

来自被嵌套的STL容器的迭代器

若你的容器中使用了STL容器作为内部数据成员,那么只需要将迭代器和两个方法的实现托付给STL容器即可。

例如,假如你有一个类CourseList,它表示学生参加的课程的列表,在其内部使用了一个Student的Vector来存储课程列表。对这样的自定义容器只需要依赖STL容器Vector即可:

换句话讲,我们做的仅仅是:

 

指针实现迭代器

C指针是合法的迭代器,因此若你的内部用来存放数据的容器是一个C数组,你需要做的仅仅就是返回指针。例,我们将StudentList实现为一个C数组:

用已有迭代器来定义新迭代器

 

有时候需要一个普通容器的迭代器的变体,比如该迭代器可以计数,或者迭代器可以检查是否在一个合法范围

定义这种特殊的迭代器通常需要使用委托模式,新迭代器的构造函数要初始化某种存在的迭代器,以及其它所需要的信息,并将某种已有的迭代器作为私有成员变量,然后这个自定义的迭代器的operator++ operator* 只是添加一些额外的功能然后调用那个已有的迭代器变量的++ 与* 操作符

 

为新容器定义迭代器

最后一种情况是为一个新类型的容器定义迭代器。

STL中不使用类的层次结构和继承,因此当定义一个迭代器时,无法以“迭代器超类”为基础开始定义。不过只要定义了部分或全部的迭代器操作符,它就可以称为是迭代器。

基本的迭代器操作符如下:

 

作为容器的例子,下面先实现一个环形队列:

环形队列

环形队列是一个有限大小的队列,并且它不会变满或超出固定大小,相反地,新元素会覆盖旧元素,一个环形队列能保持对数字流中最新的那些值的追踪,并且在添加新元素是自动地将旧元素抛弃。比如,环形队列可能用在求最近的N个数字的平均值。

我们将实现一个这样的环形队列:

像普通的队列一样,可以向队末压入元素,从队首弹出元素,但也有一些差别:

 

常规的实现环形队列的方法时用数组和两个特殊的数字:

myBegin在初始状态时为0,即数组的第一个元素。环形队列的队尾可以通过下面的计算得到:

myEnd=(myBegin+mySize) % N

可以定义如下的访问器:

改变myBegin和mySize的规则:

 

例子:

image-20211017135138809

 

环形队列的迭代器

从直观来讲,我们想把begin()和end()函数定义为:

 

但这么做是有问题的

输出:

并不能正常输出。

注意到当环形队列满的时候,myBegin=myEnd,因此下面这样的代码会失效:

一个明显的正确方法是维护一个以myBegin为基准的偏移量:

因此环形队列的迭代器必须包含两个东西:

  1. 对环形队列的引用
  2. 一个记录偏移量的变量

其运算符如下:

 

一些要注意的C++语法细节

 

细节1:让容器和迭代器建立联系

容器要和其迭代器建立联系:

二者的相互引用意味着我们要在迭代器之前定义容器,并且迭代器常常需要访问容器的私有成员才能完成功能。

出于这些原因,可归纳处定义容器和其迭代器的典型模式:

代码如下:

 

细节2:使容器的迭代器易于使用

某些STL函数,如:back_inserter(),需要一些关于容器的关键信息才能构建迭代器,这些信息由容器类开头的一系列typedef来传达,例如容器类应定义类型value_type和iterator使得其它函数可以知道这个容器可以装入什么类型的对象,以及该容器的迭代器类型是什么

 

细节3:begin()和end()

容器中的主要负责返回迭代器的函数是begin()和end()。通常,它们只是调用迭代器类的构造器,构造器一般最少需要两个参数:

 

细节4:在迭代器中存储容器的引用

 

 

细节5:为迭代器定义operator!=()

若迭代器的数据成员只是指针(表示容器)以及int等基本类型,则默认的operator==和operator!=就够用了

 

细节6:为迭代器定义operator*()

*dest被称为左值,即能存储其它值的东西。

定义operator*意味着返回一个对某位置的引用,这确保了无论对迭代器的解引用发生在等号的哪一侧都使其是合法的(引用也确保了该对象一定不是NULL的)

 

细节7:为迭代器定义operator++()

自增运算符有两种调用方式,一种是前置,一种是后置:

前置++ 的实现比较简单,只需要将迭代器中表示位置的变量增加、设为下一位置即可,并返回迭代器本身。

后置的版本有些令人困惑,原因有二:

第一个问题是将后置++的参数列表设为一个没有实际用处的int

第二个问题需要创建一个修改前的迭代器的拷贝并将其返回

 

其它C++语法上的细节