当前位置: 首页 >  焦点 >  >  详情
环球速讯:6 迭代器(上)
2023-03-07 01:51:19    来源:哔哩哔哩

本项目GitHub:HuangCheng72/HCSTL: 我的STL实现 (github.com): https://github.com/HuangCheng72/HCSTL


【资料图】

进入正文。

for_each算法在vector中实现了,也达到了我们满意的效果。

我们想到一个问题,for_each能不能在别的容器比如 list 上实现呢?这肯定是可以的,遍历一个链表是很简单的,我们可以轻而易举写出如下的代码:

list.h :

但是我们推广一下,未来还有很多种容器要开发,甚至用户自己也会实现容器,我们难道对每个容器都单独实现一个 for_each 吗?这工作量未免也太大了。

所以,需要将 for_each 作为一个单独的部分(算法),分离出来,它应该是一个适用于符合一定标准的容器的组件,只要容器符合这个标准,就可以使用 for_each 这个算法。

那么我们观察 for_each 在 vector 和 list 中实现的差别:

我们发现,输入参数的类型不同,导致 temp 的类型不同(操作的指针类型不同);

对于第一个问题,我们考虑到模板,可以用模板实现统一,可以实现像这样的代码。

但是我们很快遇到了问题,Pointer移动到下一位(递进)的方法并不一样,Pointer取出要操作的数据的方法也不一样。

C++的类与对象实践中,有时存在这么一种场景。已知一个函数,它有时候传入参数的是A类的对象,有时候传入参数的又是B类的对象,而A类和B类之间并没有相互继承的关系,函数仅用到它们都有的一个名称为Function的函数(这里只是举个例子,不代表真实情况)。如:

碰到这种问题,我们可以用的解决方法:

函数重载,即分别实现只接受A类对象的函数和只接受B类对象的函数,当接受到对应类型的对象的时候就可以调用对应的函数。

建立一个空类或者抽象类,让A类和B类都继承自这个空类或者抽象类,以这个抽象类作为函数参数的类型,利用父类引用可以指向子类对象的特点,完成统一的传参。

我们当然可以采用方法1,但是我们也要考虑到后来,新增一种容器就要重载一次或者多次,就达不到我们减少工作量增加代码复用性的目的,因此,我们选用方法2,即新建一个父类,在 vector 和 list 中分别实现其子类,封装其中 vector 的指针和 list 的结点,子类中具有同一种行为,在这里是统一的移动到下一位(递进)和取值操作函数。

实际使用中,我们封装为类或者结构体都行,C++的结构体可以视为一个全部成员变量和成员函数都默认为public的类,而且结构体名也可以作为模板参数传参。

我们要封装的这个父类,有个名字,叫做 迭代器(Iterator),迭代器应当遵照统一的标准,执行统一的行为,具体迭代器的实现则是视容器的不同而不同。迭代器的主要工作是 模仿指针,为用户或者算法操作容器数据提供一个统一的接口,让用户或者算法不需要面对各种千奇百怪的结构或者操作方法,只需要操作迭代器,就可以读写容器。因此,迭代器的最高境界就是用起来跟指针一样,C++的原生指针就是最全能的迭代器

迭代器,根据其能力大小,可以分为五种。每个容器均可以通过其中一或多种迭代器访问其中数据。

输入迭代器(单向移动,只能读一次)(例如istream_iterator)

输出迭代器(单向移动,只能写一次)(例如ostream_iterator)

正向迭代器(单向移动,读写多次)(例如单链表的迭代器)(在输入输出两个迭代器基础上扩展)

双向迭代器(双向移动,读写多次)(例如双向链表的迭代器)(在正向迭代器基础上扩展)

随机访问迭代器(在容器内可以随便移动到哪里,读写多次)(最经典就是指针)(在双向迭代器基础上扩展)

我们希望迭代器最好能像指针一样,可以通过*来读写数据,可以++,--,或者p += 3 ,p -= 4 之类的操作来移动,还可以通过两个迭代器相减计算两个迭代器之间的距离(最好同样用指针相减的结果ptrdiff_t类型表示,当然你非得用其他也行)等等。

这就需要运用C++的重载机制,在迭代器中重载这些运算符。

同时,以用户或者算法的视角来看,我们直接面对的只有迭代器,我们并不知道这个容器是什么,里面保存的数据是什么类型的,容器的具体实现原理是什么,我们能做的事情就是通过迭代器,按照我们定下的步骤去操作数据。

有些情况下,用户或者算法需要特殊的操作,比如要让迭代器向反方向移动,如果提供的迭代器是个输入输出或者正向迭代器,这种情况显然是没办法继续进行的。所以,用户和算法必须要知道这个迭代器的能力范围,因此迭代器需要保存它的种类标签信息(iterator_category,这是为了确定它的能力范围)。

用户或者算法需要知道迭代器所指向的数据的类型(value_type),C++是强类型语言,需要给出确切的类型才能定义变量(不考虑auto),用于接收迭代器所指向的数据。

用户还需要确定迭代器之间的距离表达结果的类型,这样子用户才能接收到两个迭代器之间的距离信息,不过一般都默认指定为ptrdiff_t,与指针相减的结果相同。

因此,我们所设计的迭代器的原型如下:

我们得到了迭代器,需要知道迭代器里面包含的这些信息,该怎么做?

这时候我们就想起来了类型萃取(type_traits)了,类型萃取是提取类型的信息,这里我们也是在提取迭代器的信息。

只要是符合上面标准的迭代器,都应该可以被萃取出这些信息。

其实可以让算法干这件事,但是我们希望算法只干本职工作就是计算逻辑,只要拿到迭代器就能读写,不要管迭代器的内部状态(相当于一个ATM,我只管存钱取钱,不关心你ATM内部运作或者你怎么判断假币)。

因此,我们模仿类型萃取机制,实现了一个 迭代器萃取(iterator_traits)机制。

其实就是很简单的起别名,但是确实实现了我们的功能,只要是按照迭代器规范设计的迭代器,必定可以被我们实现的迭代器萃取机制萃取出我们需要的信息,而且我们的结构体都是空结构体,并不占用内存空间,从头到尾我们都是利用了模板特化来实现的。

类型萃取的结果该怎么使用呢?以下是一种使用方法。

另外,C++11推出了decltype关键字,可以通过该关键字来直接获取类型信息,为了代码可读性,可以增加一个函数,用于获取特定的迭代器萃取结果,例如:

其用法为:

因此,我们实现的 iterator.h 头文件完整如下:

欢迎访问本项目的GitHub仓库,如果对您有帮助,麻烦给项目一个star,谢谢!

HuangCheng72/HCSTL: 我的STL实现 (github.com): https://github.com/HuangCheng72/HCSTL

关键词: 之间的距离 输入输出

上一篇:党建引领红色力量!苏州市相城区黄桥街道反诈宣防“不散场”,服务妇女家庭“不打烊”_全球球精选
下一篇:最后一页