[C++]迭代器iterator的实现(支持编译器for块简易写法)

什么是迭代器?
以下是维基百科的简介.

迭代器(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 就是本章完整源代码

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

八宝咸鱼

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值