C++模拟实现List迭代器详解

 更新时间:2022年04月15日 12:15:19   作者:m0_52012656  
list不同于其他容器,他是一个链表,物理地址并不连续。所以在实现list类的迭代器的时候,需要将迭代器单独封装到一个类里,因为需要重载很多操作符来跟其他容器的迭代器使用达成一致

概念

迭代器是一种抽象的设计概念,其定义为:提供一种方法,使他能够按顺序遍历某个聚合体(容器)所包含的所有元素,但又不需要暴露该容器的内部表现方式。

迭代器是一种行为类似智能指针的对象, 而指针最常见的行为就是内 容提领和成员 访问。 因此迭代器最重要的行为就是对operator*和operator->进行重载。

STL的中心思想在于: 将数据容器和算法分开, 彼此独立设计, 最后再以一贴胶合剂( iterator) 将它们撮合在一起。STL的迭代器是一个可遍历STL容器全部或者部分数据

迭代器使用

我们可以使用迭代器访问修改链表元素

list<int>  lt;
list<int>::iterator it=lt.begin();
while(it!=lt.end())
{
    *it+=2;
    cout<<*it<<" ";
    it++;
}

2.我们有些函数接口需要传迭代器,例如:

template <class InputIterator, class T>
   InputIterator find (InputIterator first, InputIterator last, const T& val);
​
template <class ForwardIterator, class T>
  void replace (ForwardIterator first, ForwardIterator last,
                const T& old_value, const T& new_value);

迭代器模拟实现

迭代器的大体结构

//链表节点
template<class T>
struct ListNode {
    ListNode<T>* _next;
    ListNode<T>* _prev;
    T _data;
    //构造节点值
    ListNode(const T& data = T())
        :_next(nullptr)
        ,_prev(nullptr)
        ,_data(data)
    {}
};
​
///迭代器
//T为list数据类型,Ref为T&,Ptr为T*
template<class T,class Ref,class Ptr>
struct __list_iterator
{
    typedef ListNode<T> Node;
    typedef __list_iterator<T,Ref,Ptr> self;
    Node* _node;//节点指针
    
    //接下来实现的函数都是在这个位置
};

构造函数

一般都会传过来一个节点地址

__list_iterator(Node* x)
    :_node(x)
{ }

注意迭代器的拷贝构造、赋值重载以及析构函数不需要我们自己实现,编译器实现的完全够用。

  • 拷贝构造与赋值重载:因为list迭代器本身就是一个自定义类型的指针,都是地址的拷贝与赋予。所以浅拷贝就满足使用。
  • 析构函数:因为list迭代器是借助节点指针访问修改链表,节点是链表的,不需要迭代器释放。

解引用重载

解引用重载(*)

解引用本质是根据地址拿到在这个地址的有效数据

Ref operator*()
{
    return _node->_data;
}

重载

->重载

->本质是拿到所求数据的地址

Ptr operator->()
{
    return &_node->_data;
}

自增实现

前置++

++后迭代器指向当前位置的下一个位置,返回指向下一个位置的迭代器

self& operator++()
{
    _node=_node->_next;
    return *this;
}

后置++

++后迭代器指向当前位置的下一个位置,返回指向之前位置的迭代器,要使用一个临时变量保存++之前的this指针,然后后移_node,返回临时变量。

//这块一定要使用占位符,防止与前置++重命名。
self& operator++(int)
{
    __list_iterator<T> tmp(*this);
    _node=_node->_next;
    return tmp;
}

自减实现

与++基本一样,不做解释。

前置--

self& operator--()
{
    _node=_node->_prev;
    return *this;
}

后置--

self& operator--(int)
{
    __list_iterator<T> tmp(*this);
    _node=_node->_prev;
    return tmp;
}

运算符重载

bool operator!=(const self& it)const
{
    return _node!=it._node;
}
 
bool operator==(const self& it)const
{
    return _node==it._node;
}

迭代器失效

以vector为例,当我们插入一个元素时它的预分配空间不够时,它会重新申请一段新空间,将原空间上的元素 复制到新的空间上去,然后再把新加入的元素放到新空间的尾部,以满足vector元素要求连续存储的目的。而后原空间会被系统撤销或征做他用,于是指向原 空间的迭代器就成了类似于“野指针”一样的东西,指向了一片非法区域。如果使用了这样的迭代器会导致严重的运行时错误就变得很自然了。这也是许多书上叙 述vector在insert操作后“可能导致所有迭代器实效”的原因。

但是想到这里我不禁想到vector的erase操作的叙述是“会导致指向删除元 素和删除元素之后的迭代器失效” ,这里的删除元素不一定不成功,但一定存在迭代器失效。例:

vector<int> v;//{1,2,3,4,5}
vector<int>::iterator it=v.begin();
while(it!=v.end())
{
    if(*it%2==0)
    {
        v.erase(it);
    }
    it++;
}

所以要避免这种情况,改进代码

vector<int> v;//{1,2,3,4,5}
vector<int>::iterator it=v.begin();
while(it!=v.end())
{
    if(*it%2==0)
    {
        v.erase(it);
    }
    else
        it++;
}

list迭代器失效

list<int> l1;
list<int>::iterator it=l1.begin();
while(it!=l1.end())
{
    if(*it%2==0)
    {
        l1.erase(it);
    }
    else
        ++it;
}

改进代码

list<int> l1;
list<int>::iterator it=l1.begin();
while(it!=l1.end())
{
    if(*it%2==0)
    {
        it=l1.erase(it);
    }
    else
        ++it;
}

归纳迭代器失效的类型

(1)由于容器元素整体“迁移”导致存放原容器元素的空间不再有效,从而使得指向原空间的迭代器失效。

(2)由于删除元素使得某些元素次序发生变化使得原本指向某元素的迭代器不再指向希望指向的元素

模拟List

具体下一章讲

 template<class T>
    class list
    {
        typedef ListNode<T> Node;
    public:
        typedef __list_iterator<T, T&, T*> iterator;
        typedef __list_iterator<T, const T&, const T*> const_iterator;
        iterator begin()
        {
            return iterator(_head->_next);
        }
​
        iterator end()
        {
            return iterator(_head);
        }
​
        const_iterator begin()const
        {
            return const_iterator(_head->_next);
        }
​
        const_iterator end()const
        {
            return const_iterator(_head);
        }
​
        list()
        {
            _head = new Node();
            _head->_next = _head;
            _head->_prev = _head;
        }
        void push_back(const T& x)
        {
            Node* tail = _head->_prev;
            Node* newnode = new Node(x);
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;
        }
​
        void insert(iterator pos, const T& x)
        {
​
        }
        void erase(iterator pos)
        {
​
        }
    private:
        Node* _head;
    };

到此这篇关于C++模拟实现List迭代器详解的文章就介绍到这了,更多相关C++ List迭代器内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • C++11中value category(值类别)及move semantics(移动语义)的介绍

    C++11中value category(值类别)及move semantics(移动语义)的介绍

    这篇文章主要给大家介绍了C++11中value category(值类别)及move semantics(移动语义)的介绍,文中介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-05-05
  • C中的open(), write(), close(), fopen()详解

    C中的open(), write(), close(), fopen()详解

    本文主要介绍了C语言中的open(), write(), close(), fopen()等文件操作函数,open()函数用于打开文件,write()函数用于写入数据,close()函数用于关闭已打开的文件描述符
    2024-10-10
  • 使用VS2022开发在线远程编译部署的C++程序(图文详解)

    使用VS2022开发在线远程编译部署的C++程序(图文详解)

    这篇文章主要介绍了使用VS2022开发可以在线远程编译部署的C++程序,本文分步骤通过图文并茂的形式给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-12-12
  • C++ Sqlite3的使用方法

    C++ Sqlite3的使用方法

    这篇文章主要介绍了C++ Sqlite3的使用方法,文中讲解非常细致,代码帮助大家更好的理解和学习,感兴趣的朋友可以了解下
    2020-06-06
  • C++读取单个字符操作示例详解

    C++读取单个字符操作示例详解

    这篇文章主要为大家介绍了C++读取单个字符操作示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-09-09
  • C语言中判断int,long型等变量是否赋值的方法详解

    C语言中判断int,long型等变量是否赋值的方法详解

    声明了int ,long型等局部变量,在利用一些方法给这些变量赋值之后,想判断这些变量是不是真的被赋初值了,怎么办
    2013-07-07
  • C语言实现文件读写操作的几种常用方法

    C语言实现文件读写操作的几种常用方法

    C语言提供了一系列文件操作函数,使得我们可以通过程序对文件进行读写操作,本文主要介绍了C语言实现文件读写操作的几种常用方法,具有一定的参考价值,感兴趣的可以了解一下
    2024-03-03
  • C语言动态内存管理介绍

    C语言动态内存管理介绍

    大家好,本篇文章主要讲的是C语言动态内存管理介绍,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • 定义vim配置文件vimrc用于c/c++编程

    定义vim配置文件vimrc用于c/c++编程

    vim作为Linux下广受赞誉的代码编辑器,其独特的纯命令行操作模式可以很大程度上方便编程工作,通过自定义vim配置文件可以实现对vim功能的个性化设置。这篇文章主要介绍了定义vim配置文件vimrc,用于c/c++编程 ,需要的朋友可以参考下
    2018-10-10
  • C++实现读取特定路径下文件夹及文件名的方法

    C++实现读取特定路径下文件夹及文件名的方法

    这篇文章主要介绍了C++实现读取特定路径下文件夹及文件名的方法,需要的朋友可以参考下
    2014-07-07

最新评论