C++模拟实现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迭代器内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • C++设计模式之迭代器模式

    前言 又到年底了,时间真的过的好快啊.最近也非常感伤,总是怀念大学的日子,做梦的时候也常常梦到.梦到大学在电脑前傻傻的敲着键盘,写着代码,对付着数据结构与算法的作业:建立一个链表,遍历链表,打印链表.现在把那个时候声明的链表的头文件拿出来看看: 复制代码 代码如下: typedef struct tagNode {      int value;      tagNode *pPre;      tagNode *pNext; }Node;   class CList { public:    

  • C++特性:迭代器

    1. 迭代器(Iterator)的介绍 背景:指针可以用来遍历存储空间连续的数据结构,但是对于存储空间费连续的,就需要寻找一个行为类似指针的类,来对非数组的数据结构进行遍历. 定义:迭代器是一种检查容器内元素并遍历元素的数据类型. 迭代器提供对一个容器中的对象的访问方法,并且定义了容器中对象的范围. 迭代器(Iterator)是指针(pointer)的泛化,它允许程序员用相同的方式处理不同的数据结构(容器). (1)迭代器类似于C语言里面的指针类型,它提供了对对象的间接访问. (2)指针是C语言

  • C++迭代器iterator详解

    目录 1.迭代器分类 1) 正向迭代器 2) 常量正向迭代器 3) 反向迭代器 4) 常量反向迭代器 2.迭代器用法示例 3.迭代器:++it 与 it++ 哪个好? (1)前置返回一个引用,后置返回一个对象 (2)前置不会产生临时对象,后置必须产生临时对象,临时对象会导致效率降低 4.迭代器的功能分类 5.迭代器的辅助函数 总结 1.迭代器分类 要访问顺序容器和关联容器中的元素,需要通过"迭代器(iterator)"进行.迭代器是一个变量,相当于容器和操纵容器的算法之间的中介.迭代器

  • C++ 模拟实现list(迭代器)实现代码

    C++ 模拟实现list(迭代器) 实现代码: #pragma once; #include <assert.h> #include<iostream> #include <assert.h> using namespace std; template<class T> struct __ListNode { T _data; __ListNode<T>* _next; __ListNode<T>* _prev; __ListNode

  • C++ 实现自定义类型的迭代器操作

    ##动机 我们知道STL实现了很多算法(#include<algorithm>),如果项目是基于STL构建那么能够最大化使用现有代码当然是最好的.在STL中容器和算法之间的桥梁是迭代器.所以在定义好自定义类型的容器后,接下来就是迭代器的实现. STL中的迭代器 迭代器模式是一种经典的设计模式,而STL的迭代器实现用到了模板的一些特性和技能,在这里稍微介绍一下 下面是STL中结构体iterator的定义,这么定义是给后面的算法多态和萃取时(具体见书中介绍)使用的. 其中的_Category 和_

  • C/C++迭代器的失效问题详解

    目录 前言 下面是我今天做的一些代码测试: 我们接着往下看下一个出问题的测试代码: 迭代器失效 总结 前言 我今天在使用迭代器时发现了一个问题,这个问题就是我在使用的迭代器时发现莫名其妙的有越界访问和获取的位置跟预期不符,经过一天的排查我发现不是所有情况下会出现这种问题,而是在容器删除和扩容时会发生越界或结果和预期不符的情况. 下面是我今天做的一些代码测试: Text1 该函数的功能是把数组里面的所有偶数删除,遍历方式使用的是迭代器. #include <iostream> #include

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

    目录 概念 迭代器使用 迭代器模拟实现 迭代器的大体结构 构造函数 解引用重载 重载 自增实现 自减实现 运算符重载 迭代器失效 模拟List 概念 迭代器是一种抽象的设计概念,其定义为:提供一种方法,使他能够按顺序遍历某个聚合体(容器)所包含的所有元素,但又不需要暴露该容器的内部表现方式. 迭代器是一种行为类似智能指针的对象, 而指针最常见的行为就是内 容提领和成员 访问. 因此迭代器最重要的行为就是对operator*和operator->进行重载. STL的中心思想在于: 将数据容器和算法

  • C++模拟实现vector流程详解

    目录 模拟vector 总结 模拟vector 我们可以通过模板实现类似vector的类.我们实现一个StrVecTemp类,其内部通过allocator开辟空间,存储的类型用T来表示,T是模板类型. template <typename T> class StrVecTemp { public: StrVecTemp() : elements(nullptr), first_free(nullptr), cap(nullptr) {} //拷贝构造函数 StrVecTemp(const St

  • Ruby中的迭代器详解

    D瓜哥最近想做一个网站,另外,老早就有学习一门动态语言的想法,满足着两个条件的编程语言中,Ruby.Python是最合适的两种语言.现在Ruby on Rails如日中天,光芒万丈!所以,就选定了Ruby,从零开始学习. 前天看了Ruby的迭代器,对于我这个只学过Java.C/C++等的人来说,绝对是眼前一亮的感觉!而且是光彩夺目:没想到迭代器还可以这么玩,太简练太方便而且特别强大!然后,D瓜哥就迫不及待的想写一篇文章给大家介绍介绍Ruby的迭代器! 迭代器简介 先简单介绍一下迭代器. 1.一个

  • java爬虫模拟登陆的实例详解

    使用jsoup工具可以解析某个URL地址.HTML文本内容,是java爬虫很好的优势,也是我们在网络爬虫不可缺少的工具.本文小编带领大家使用jsoup 实现java爬虫模拟登陆,通过省力的API,很好的实现java爬虫模拟登陆. 一.使用工具:Jsoup jsoup 是一款Java 的HTML解析器,可直接解析某个URL地址.HTML文本内容.它提供了一套非常省力的API,可通过DOM,CSS以及类似于jQuery的操作方法来取出和操作数据. 二.实现java爬虫模拟登陆 1.确定想要爬取的ur

  • C语言用栈模拟实现队列问题详解

    目录 题目描述 题目链接 思路分析 代码实现 题目描述 请你仅使用两个栈实现先入先出队列.队列应当支持一般队列支持的所有操作(push.pop.peek.empty). 你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的. 题目链接 用栈实现队列 思路分析 题目的意思是要用两个栈来模拟实现一个队列.仅可以用栈的基本功能实现队列的基本功能.所以需要创建两个栈.所以这两个栈st1,st2可用一个结构

  • Python学习之迭代器详解

    目录 什么是迭代器 如何生成迭代器 迭代器函数 - iter() 函数 与 next() 函数 可迭代的对象 生成迭代器 迭代器的用法 - 演示案例 什么是迭代器 迭代是 python 中访问集合元素的一种非常强大的一种方式.迭代器是一个可以记住遍历位置的对象,因此不会像列表那样一次性全部生成,而是可以等到用的时候才生成,因此节省了大量的内存资源.迭代器对象从集合中的第一个元素开始访问,直到所有的元素被访问完.迭代器有两个方法:iter()和 next()方法. 这么解释可能不太直观,我们以生活

  • C语言 模拟实现strlen函数详解

    目录 前言 一.strlen函数的介绍 1.strlen函数的声明 2.strlen函数的简单运用 3.注意事项 二.三种实现strlen函数的方法 1.计数器的方法 2.递归方法 3.指针-指针的方法 前言 用C语言模拟实现strlen函数,我这里有三种方法,快来看看跟你用的方法是否是一样. 一.strlen函数的介绍 1.strlen函数的声明 size_t strlen ( const char * str ): 这里函数的返回值为无符号整形(size_t),传入的是一个常量char*类型

  • Vue echarts模拟后端数据流程详解

    目录 KOA2的使用 安装 Koa app.js入口文件 KOA2的使用 KOA2是由Express 原班人马打造. 环境依赖 Node v7.6.0 及以上. 支持 async 和 await 洋葱模型的中间件 写响应函数(中间件) 响应函数是通过use的方式才能产生效果, 这个函数有两个参数, ctx :上下文, 指的是请求所处于的Web容器,我们可以通过 ctx.request 拿到请求对象, 也可 以通过 ctx.response 拿到响应对象 next :内层中间件执行的入口 模拟服务

  • JS面试之异步模拟超时重传机制详解

    目录 引言 题目分析 代码设计 核心讲解 引言 前面我讲解了两篇有关异步的逻辑思维题目,一个是红绿灯转换,还有一个是异步并发限制.有小伙伴私信我说不过瘾,希望还能再出一篇异步超时重传的讲解.为了满足这位粉丝的小小要求(我尼玛),我查询了相关资料和面试题,确实发现这是某大肠面试的代码设计题.不得不说这位粉丝发现的这个题目是相当亮眼,相当给力. 题目分析 超时重传机制,看到这个词语想必科班同学都十分十分熟悉吧.大家第一时间肯定会想到计算机网络中tcp的超时重传.不错,此处的异步模拟超时重传机制和计算

  • Java多线程模拟银行系统存钱问题详解

    目录 一.题目描述 二.解题思路 三.代码详解 多学一个知识点 一.题目描述 题目:模拟一个简单的银行系统,使用两个不同的线程向同一个账户存钱. 实现:使用特殊域变量volatile实现同步. 二.解题思路 创建一个类:SynchronizedBankFrame,继承JFrame类 写一个内部类Bank 定义一个account变量,来表示账户. deposit():一个存钱的方法 getAccount():显示账户余额的方法. 写一个内部类Transfer,实现Runnable接口 在run方法

随机推荐