什么是迭代器?
以下是维基百科的简介.
迭代器(iterator),是确使用户可在容器对象(container,例如链表或数组)上遍访的对象[1][2][3],设计人员使用此接口无需关心容器对象的内存分配的实现细节。其行为很像数据库技术中的光标(cursor),迭代器最早出现在1974年设计的CLU编程语言中。
在各种语言实现迭代器的方式皆不尽同,有些面向对象语言像Java、C#、Ruby、Python、Delphi都已将迭代器的特性内置语言当中,完美的跟语言集成,我们称之隐式迭代器。但像是C++语言本身就没有迭代器的特色,但STL仍利用模板实现了功能强大的迭代器。STL容器的数据的内存地址可能会重新分配(reallocate),与容器绑定的迭代器仍然可以定位到重新分配后的正确的内存地址。
用迭代器,我们可以很方便的对容器内的每个数据进行遍历读写操作.
但是C++作为基于C语言来开发的语言,它继承了很多C语言的有点,比如自由
你只要不让底层逻辑混乱,那么你就可以随心所需写你所想的一切.
就如同wiki百科上所说,C++本身并没有迭代器这个东西.
而我们使用的STL容器,支持迭代器,也仅是因为他人已经为我们造好了车轮.
我们拿来用就可以.
但如果我们想自己制造一个类似的车轮呢?
C++可没有程序员编程指南,告诉你它身上所有的零部件都是怎么造的
(网上大部分关于C++STL技术的讲解都是复现,不是复制)
本章,就是让我实现自己的迭代器,并且支持for块简易遍历写法.
先搞一个双向链表.
(当然你也可以写一个hashmap,array,vector之类的数据容器,只要是数据容器,那么都可以复现属于自己的迭代器)
下面是双向链表的链接,你可以学习如何写一个自己的双向链表,或者是过去下载一份我已经写好的源代码.
https://blog.csdn.net/qq_42468226/article/details/116530424
如果你不想用之前的双向链表,我也给你附带了本章的链表代码.
下面是节点类
using UINT = unsigned int;
template <typename dataType> // 节点类
class Node
{
dataType data;
Node* next;
Node* back;
public:
dataType GetData() const;
dataType& SetData(dataType data = NULL);
Node* GetNextP() const;
void SetNextP(Node* node);
Node* GetBackP() const;
void SetBackP(Node* node);
public:
Node(dataType data = NULL, Node* next = nullptr, Node* back = nullptr);
};
这是节点类外成员定义
template <typename dataType>
dataType Node<dataType>::GetData() const
{
return this->data;
}
template <typename dataType>
dataType& Node<dataType>::SetData(dataType data)
{
if (data)
this->data = data;
return this->data;
}
template <typename dataType>
Node<dataType>* Node<dataType>::GetNextP() const
{
return this->next;
}
template <typename dataType>
void Node<dataType>::SetNextP(Node* node)
{
this->next = node;
}
template <typename dataType>
Node<dataType>* Node<dataType>::GetBackP() const
{
return this->back;
}
template <typename dataType>
void Node<dataType>::SetBackP(Node* node)
{
this->back = node;
}
template <typename dataType>
Node<dataType>::Node(dataType data, Node* next, Node* back) : data(data), next(next), back(back)
{
}
链表类
template <typename dataType> // 链表类
class LinkList
{
static UINT size;
protected:
Node<dataType>* headPoint; // 头节点
Node<dataType>* tailPoint; // 尾节点
Node<dataType>* realTail; // 它的next是头,back是尾 作为迭代器的end
public:
explicit LinkList(dataType data = NULL, Node<dataType>* headPoint = nullptr, Node<dataType>* tailPoint = nullptr);
LinkList(dataType* arrFAddress, UINT arrBorder, Node<dataType>* headPoint = nullptr,
Node<dataType>* tailPoint = nullptr);
};
链表类外定义
//---------------------------------链表类外定义
template <typename dataType> //单头结点构造函数
LinkList<dataType>::LinkList(dataType data, Node<dataType>* headPoint, Node<dataType>* tailPoint) : headPoint(headPoint),
tailPoint(this->headPoint)
{
if (!this->headPoint)
this->headPoint = new Node<dataType>{ data };
else
this->headPoint->SetData(data);
this->tailPoint = this->headPoint;
this->tailPoint->SetNextP(new Node<dataType>{ 0,this->headPoint,this->tailPoint });
this->realTail = this->tailPoint->GetNextP();
}
template <typename dataType>
LinkList<dataType>::LinkList(dataType* arrFAddress, UINT arrBorder, Node<dataType>* headPoint, // 数组构造函数
Node<dataType>* tailPoint) : headPoint(headPoint), tailPoint(this->headPoint)
{
if (!headPoint)
this->headPoint = new Node<dataType>;
Node<dataType>* tmpPoint{ this->headPoint };
for (UINT count = 0; count < arrBorder; count++)
{
if (count == 0)
{
tmpPoint->SetData(*arrFAddress++);
continue;
}
tmpPoint->SetNextP(new Node<dataType>{ *arrFAddress++ });
tmpPoint->GetNextP()->SetBackP(tmpPoint);
tmpPoint = tmpPoint->GetNextP();
}
this->tailPoint = tmpPoint;
this->tailPoint->SetNextP(new Node<dataType>{0,this->headPoint,this->tailPoint});
this->realTail = this->tailPoint->GetNextP();
}
代码附送先告一段落
下面开始手把手的复现迭代器了!
既然是复现STL迭代器,那么我就先再main函数里面,弄一个STL容器.
我拿vector来举例子.
这就是迭代器遍历读写容器的例子.
上面两个for循环,第一个是for块简易写法,第二个则是迭代器标准写法.
千万不要认为这是两种不同的方式,它们就是一模一样的东西,只是C++是个爱骗人的语言.
所以所看非真,请记住这句话.
它们都可以成功的遍历所有项.
根据第二个for块的标准迭代器写法,
我们可以分析出,迭代器,是个嵌套类.
那么,我们就可以去链表类内实现一个iterator类
class iterator // 迭代器类 注意这是写在数据容器内的,不要把迭代器单独写一个类,不然就实现不出来类似STL的效果了
{
public:
Node<dataType>* p; // 节点类指针
public:
iterator(Node<dataType>* p = nullptr);
dataType operator*() const;
Node<dataType>* operator->() const;
iterator& operator++();
iterator operator++(int);
bool operator==(const iterator& arg) const;
bool operator!=(const iterator& arg) const;
};
// return头节点
iterator begin() const //这两个对象是负责界定边界的,begin返回容器头节点,end返回容器尾结点
{
return iterator(realTail->GetNextP());
}
// return尾结点之后的空白节点,已实现for块一次性全部遍历
// 不要直接return还有数据的尾结点,你应该在有数据的尾结点后面,new出个空白尾结点,作为返回值,不然你就遍历不到有数据的尾结点了!
iterator end() const
{
return iterator(realTail);
}
//---------------------------------链表嵌套迭代器类外定义
template <typename dataType>
LinkList<dataType>::iterator::iterator(Node<dataType>* p):
p(p)
{
}
template <typename dataType>
dataType LinkList<dataType>::iterator::operator*() const
{
return p->GetData();
}
template <typename dataType>
Node<dataType>* LinkList<dataType>::iterator::operator->() const
{
return p;
}
template <typename dataType>
typename LinkList<dataType>::iterator& LinkList<dataType>::iterator::operator++()
{
p = p->GetNextP();
return *this;
}
template <typename dataType>
typename LinkList<dataType>::iterator LinkList<dataType>::iterator::operator++(int)
{
Node<dataType>* tmp = p;
p = p->GetNextP();
return iterator(tmp);
}
迭代器虽然功能强大,但是它无非是包含一个指针和运算符重载的类罢了
即便不用专门写一个迭代器,我们也可以通过头尾指针进行遍历.
但多掌握一门技术,也不是坏事,并且,迭代器的for块简易写法,真的很爽!
下面来分析迭代器的原理.
首先,迭代器类,以下简称it类
it类成员由一个你需要用到的数据类型指针.
这个指针负责保存地址.
至于剩下运算符重载,是为了实现 比较 自增 自减.
有了it类之后,我们就可以在数据容器类里面定义两个类成员函数.
返回值是it类.一个是begin函数,一个是end函数.
这两个函数负责界定头尾.
以实现 it = xxx.begin();it!=xxx.end();这样的逻辑表达式
下面来测试咱们自己的数据容器迭代器.
如图所示,我们不仅支持标准写法.
编译器也能识别出我们的迭代器,进而可以建议写法.
面试知识点:迭代器的原理是什么,如何运用
答:对指针的运用,以划定出数据容器的边界,通过使用指针,来快速进行数据容器项的读写遍历操作.
完整代码:https://github.com/Babaoxianyu/BlogSpotSource
找到 Class 就是本章完整源代码