C++之STL-list+模拟实现

目录

一、list的介绍和基本使用的方法

1.1 list的介绍

1.2 list的基本使用方法

1.2.1  构造方法

 1.2.2 迭代器

1.2.3 容量相关的接口

1.2.4 增删查改的相关接口

1.3 关于list迭代器失效的问题

二、模拟实现list

2.1 节点类

2.2 迭代器类

2.3 主类list类

2.3.1 成员变量的建立

 2.3.2 迭代器的建立和相关接口的实现

2.3.3 增删查改以及容量接口的实现

1.size()

 2.empty()

3.insert()

4.push_back()

5.pop_back()

6.erase()

7.clear()

8.swap()

2.3.4 构造函数和析构函数


一、list的介绍和基本使用的方法

1.1 list的介绍

同样的给大家上图。

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
  3. . list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。
  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。
  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;(简单来说就是不支持随机访问)。

1.2 list的基本使用方法

1.2.1  构造方法

常用的构造方法主要是以下4种:

  1. list (size_type n, const value_type& val = value_type())   
    构造的list中包含n个值为val的元素
    list<int> st(10,1);
  2. list()
    构造空的list
    list<int>  st;
  3. list (const list& x)
    拷贝构造函数
    list<int>  l1(10,1);
    list<int>  l2(l1);
  4. list (InputIterator first, InputIterator last)
    用[first, last)区间中的元素构造list,即使用迭代器进行构造
    list<int>  l1(10,1);
    list<int>  l2(l1.begin(),l1.end());

 1.2.2 迭代器

       对于list的迭代器,我们可以暂且认为其是指向list中某个节点的指针,等到模拟实现的时候再给大家详细介绍。这里大概给大家画一下begin()和end()的指向。

1.2.3 容量相关的接口

  1. empty()
    检测list是否为空,是返回true,否则返回false
  2. size()
    返回list中有效节点的个数

1.2.4 增删查改的相关接口

  1. push_front()

    在list首元素前插入值为val的元素
  2.  pop_front()

    删除list中第一个元素

  3. push_back()
    在list尾部插入值为val的元素
  4. pop_back()
    删除list中最后一个元素
  5. insert()
    在list position 位置中插入值为val的元素
  6. erase()
    删除list position位置的元素
  7. swap()
    交换两个list中的元素
  8. clear()
    清空list中的有效元素

1.3 关于list迭代器失效的问题

       迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
 

二、模拟实现list

2.1 节点类

       这里采取的是带头双向列表,如果你不是很了解的话可以参考一下http://t.csdnimg.cn/uSKci
我的这篇文章。

       话不多说,上代码。

template<class T>struct ListNode{ListNode(const T& val = T()):_pPre(nullptr),_pNext(nullptr),_val(val){}ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};

       由于不知道模板参数,所以我们的默认初始化不能定死,而是采用了一个匿名对象,原本内置类型是没有类似于 T() 这种构造方式的,但是为了方便我们类的定义,祖师爷们特意进行了添加。

2.2 迭代器类

        当大家看到这个标题的时候就可能会疑惑了,为什么要重新定义一个迭代器类呢,为什么不像vector一样,直接对节点重命名不久好了吗?这确实是一个值得我们探讨的问题,要回答这个问题,我们还是要回到迭代器的本质,我在上节就有提到过:

       迭代器提供了一种统一的访问数据结构(如容器、数组、集合等)元素的方式,使得我们能够以统一的接口遍历和操作数据结构中的元素,而不必关心底层数据结构的实现细节。

       如果要满足这个需求,简单的重命名可不好使呀,首先就是范围for你无法支持,vector可以是因为其在内存中是连续的,其++和--都不需要进行重载,而list就不行,因为list所存储数据的内存是不连续的,你要是用老一套的方法肯定是不行的,所以我们才需要将list的迭代器单独封装起来,这恰恰就体现了C++中面向对象的思想。
       解决这个问题之后,我们又迎来了新的问题,const 迭代器我们该怎么实现呢?这时你肯定会说直加个const不就行了吗,答案是显而易见的,肯定不行,不然我就不会问了。那么为什么呢,那是因为这个迭代器是我们封装实现的,我们在list类中直接加const的话,相当于是对我们做的迭代器实例化出来的对象加了个const,那就是对我们所封装的节点加了个const,但节点是指针啊,要清楚,并不是指针不能动(不然范围for又死了),而是所指向的数据域不能改变,那我们应该怎么办呢?这里就需要好好的使用我们的模板了,看我的解决方案。    

 template<class T, class Ref, class Ptr>class ListIterator{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;public:ListIterator(PNode pNode = nullptr):_pNode(pNode){}ListIterator(const Self& l):_pNode(l._pNode){}Ref operator*(){return _pNode->_val;}Ptr operator->(){return &_pNode->_val;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self tmp(*this);_pNode = _pNode->_pNext;return tmp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self operator--(int){Self tmp(*this);_pNode = _pNode->pPre;return tmp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return _pNode == l._pNode;}PNode _pNode;};

       可以看的我的模板有三个参数template<class T, class Ref, class Ptr> 那这玩意有什么用呢,这后面两个参数主要作用于该类有返回节点数据域的成员函数,const对象和非const对象最大的区别就是一个内容无法被修改一个不受限制,而我们通过对节点的封装,使得外界通过迭代器访问我们数据域时,就一定会访问我们返回节点数据域的重载函数,而这样恰恰给我们模板提供了机会,我们可以根据模板传参去重载我们的返回值,这样也就实现了我们const迭代器的建立。但是这不是最妙的地方,因为要实现这个要求我们也可以复制粘贴改一下条件多写一个类就可以了,可当我们使用了模板之后,你会发现这部分工作就交给编译器去做了,实现了我们的代码复用,这就是泛型编程的好处。

2.3 主类list类

2.3.1 成员变量的建立

       这里我们主要的成员变量就是我们的哨兵位节点和我们有效节点size,为了方便初始化,我还加了一个专门初始化头节点的成员函数。

        private:void CreateHead(){_pHead = new Node();_pHead->_pNext = _pHead;_pHead->_pPre = _pHead;_size = 0;}PNode _pHead;size_t _size;

 2.3.2 迭代器的建立和相关接口的实现

有关迭代器的主要内容已经在刚刚讲的差不多了,这里我就不在过多的讲解了,直接上代码。

 public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin(){return _pHead->_pNext;}iterator end(){return _pHead;}const_iterator begin() const{return _pHead->_pNext;}const_iterator end() const{return _pHead;}

2.3.3 增删查改以及容量接口的实现

       这里接口的实现和我们双向链表以及vector的实现方式很像,我这里就不啰嗦了,有问题可以参考http://t.csdnimg.cn/uSKci和http://t.csdnimg.cn/N3bhY或者私信问我也是可以的哦。

1.size()
        size_t size()const{return _size;}
 2.empty()
        bool empty()const{return _size == 0;}
3.insert()
iterator insert(iterator pos, const T& val){PNode tmp = new Node(val);PNode cur = pos._pNode;PNode pre = pos._pNode->_pPre;tmp->_pNext = cur;tmp->_pPre = pre;pre->_pNext = tmp;cur->_pPre = tmp;_size++;return tmp;}
4.push_back()
        void push_back(const T& val) { insert(end(), val); }
5.pop_back()
        void pop_back(){ erase(--end()); }
6.erase()
        iterator erase(iterator pos){PNode Next = pos._pNode->_pNext;PNode pre = pos._pNode->_pPre;pre->_pNext = Next;Next->_pPre = pre;_size--;return Next;}
7.clear()
void clear(){iterator it = begin();while (it != end()){it = erase(it);}}
8.swap()
        void swap(list<T>& l){std::swap(_pHead, l._pHead);std::swap(_size, l._size);}

2.3.4 构造函数和析构函数

废话不多说,直接开造!

list(){CreateHead();}list(int n, const T& value = T()){CreateHead();while (n--){push_back(value);}}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);first++;}}list(const list<T>& l){CreateHead();for (auto ch : l){push_back(ch);}}list<T>& operator=(list<T> l) {CreateHead();swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;_size = 0;}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/bicheng/5159.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

多线程同步

1.多线程并发 1).多线程并发引例 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <assert.h> #include <pthread.h>int wg0; void *fun(void *arg) {for(int i0;i<1000;i){wg;printf("wg%d\n",wg);} } in…

tp6.0 rabbitmq死信队列

rabbitMq交换机&#xff0c;队列情况&#xff0c;先手动创建 1. 创建普通交换机exchange&#xff0c;普通队列order_queue_expire&#xff0c;队列设置属性&#xff1a; 消息过期时间&#xff1a;60000毫秒&#xff0c;过期绑定dead_exchange交换机&#xff0c;routing_key:de…

web前端学习笔记2

2. 网页穿上美丽外衣 2.0 代码地址 https://gitee.com/qiangge95243611/java118/tree/master/web/day02 2.1 什么是CSS CSS (Cascading Style Sheets,层叠样式表),是一种用来为结构化文档(如 HTML 文档或 XML 应用)添加样式(字体、间距和颜色等)的计算机语言,CSS 文…

文件系统学习

软连接&#xff1a;可以跨不同的磁盘块&#xff0c;创建出不同的inode节点 应连接&#xff1a;相同的inode节点&#xff0c;不同的文件名字记录在父亲节点目录中 分区(fdisk)&#xff0c;格式化(mkfs)&#xff0c;挂载(mount)&#xff0c;大于2T分区&#xff08;parted&#…

FSNotes for Mac v6.7.1中文激活版:强大的笔记管理工具

FSNotes for Mac是一款功能强大的文本处理与笔记管理工具&#xff0c;为Mac用户提供了一个直观、高效的笔记记录和整理平台。 FSNotes for Mac v6.7.1中文激活版下载 FSNotes支持Markdown语法&#xff0c;使用户能够轻松设置笔记格式并添加链接、图像等元素&#xff0c;实现笔记…

基于H.264的RTP打包中的组合封包以及分片封包结构图简介及抓包分析;FU-A FU-B STAP-A STAP-B简介;

H.264视频流的RTP封装类型分析&#xff1a; 前言&#xff1a; 1.RTP打包原则&#xff1a; RTP的包长度必须要小于MTU(最大传输单元)&#xff0c;IP协议中MTU的最大长度为1500字节。除去IP报头&#xff08;20字节&#xff09;、UDP报头&#xff08;8字节&#xff09;、RTP头&a…

C#编程模式之装饰模式

创作背景&#xff1a;朋友们&#xff0c;我们继续C#编程模式的学习&#xff0c;本文我们将一起探讨装饰模式。装饰模式也是一种结构型设计模式&#xff0c;它允许你通过在运行时向对象添加额外的功能&#xff0c;从而动态的修改对象的行为。装饰模式本质上还是继承的一种替换方…

设计模式 基本认识

文章目录 设计模式的作用设计模式三原则设计模式与类图设计模式的分类 设计模式的作用 设计模式是在软件设计过程中针对常见问题的解决方案的一种通用、可重用的解决方案。设计模式提供了一种经过验证的方法&#xff0c;可以帮助开发人员解决特定类型的问题&#xff0c;并在软…

【论文阅读】IPT:Pre-TrainedImageProcessingTransformer

Pre-TrainedImageProcessingTransformer 论文地址摘要1. 简介2.相关作品2.1。图像处理2.2。 Transformer 3. 图像处理3.1. IPT 架构3.2 在 ImageNet 上进行预训练 4. 实验4.1. 超分辨率4.2. Denoising 5. 结论与讨论 论文地址 1、论文地址 2、源码 摘要 随着现代硬件的计算能…

mybatis工程需要的pom.xml,以及@Data 、@BeforeEach、@AfterEach 的使用,简化mybatis

对 “mybatis - XxxMapper.java接口中方法的参数 和 返回值类型&#xff0c;怎样在 XxxMapper.xml 中配置的问题” 这篇文章做一下优化 这个pom.xml文件&#xff0c;就是上面说的这篇文章的父工程的pom.xml&#xff0c;即&#xff1a;下面这个pom.xml 是可以拿来就用的 <?…

7天入门Android开发之第1天——初识Android

一、Android系统 1.Linux内核层&#xff1a; 这是安卓系统的底层&#xff0c;它提供了基本的系统功能&#xff0c;如内存管理、进程管理、驱动程序模型等。安卓系统构建在Linux内核之上&#xff0c;借助于Linux的稳定性和安全性。 2.系统运行库层&#xff1a; 这一层包括了安卓…

GITEE本地项目上传到远程

由于需要&#xff0c;我这边将本地的仓库上传至GITEE。之前在网上搜索了相关的文档&#xff0c;但是步骤很繁琐&#xff0c;我这边介绍一个非常简单的。 一、在GITEE新建仓库 跟着指引一步步新建。 二、打开本地仓库&#xff0c;删除.git文件 默认情况下不会有这个.git文件&a…

【保姆级讲解如何安装与配置Xcode】

&#x1f308;个人主页: 程序员不想敲代码啊 &#x1f3c6;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f44d;点赞⭐评论⭐收藏 &#x1f91d;希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出指正&#xff0c;让我们共…

在kuboard中添加k8s集群

1.登录kuboard后&#xff0c;点击添加集群面板 系统会跳转到k8s集群添加页面&#xff0c;按照页面提示输入自身的集群信息即可&#xff0c;此处没有什么难点。 添加成功后&#xff0c;点击集群面板&#xff0c;然后点击集群概要信息&#xff0c;就可以查看集群节点信息。 集群节…

ssm092基于Tomcat技术的车库智能管理平台+jsp

车库智能管理平台设计与实现 摘 要 现代经济快节奏发展以及不断完善升级的信息化技术&#xff0c;让传统数据信息的管理升级为软件存储&#xff0c;归纳&#xff0c;集中处理数据信息的管理方式。本车库智能管理平台就是在这样的大环境下诞生&#xff0c;其可以帮助管理者在短…

Java字符缓冲区

字符缓冲区是在计算机编程中非常重要的一种数据结构&#xff0c;它主要用于存储和高效地操作字符序列。 在 Java 中&#xff0c;StringBuffer类就是典型的字符缓冲区实现。与String类不同&#xff0c;StringBuffer具有动态可变性&#xff0c;这意味着我们可以在原有的字符序列…

设计不外流,保护创意的同时锁住图纸安全!

在设计行业中&#xff0c;图纸和创意文稿的安全至关重要&#xff0c;因为它们体现了企业的创新能力和核心竞争力。华企盾DSC数据防泄密系统提供了一系列功能&#xff0c;可以有效地保护这些珍贵的设计和文档不被外泄。以下是如何利用华企盾DSC系统保障设计图纸安全的关键措施&a…

MySQL怎么看死锁记录

这个结果分成三部分&#xff1a; (1) TRANSACTION&#xff0c;是第一个事务的信息&#xff1b; (2) TRANSACTION&#xff0c;是第二个事务的信息&#xff1b; (3)WE ROLL BACK TRANSACTION (1)&#xff0c;是最终的处理结果&#xff0c;表示回滚了第一个事务。 第一个事务的信…

基于飞腾D2000全国产化高速公路一体化收费站解决方案:站数据服务器、站AI服务器、收费系统、监控系统

高速公路一体化收费站解决方案 行业 交通工程及沿路设施作为公路的一个重要组成部分&#xff0c;对城市互联和城市发展具有重要意义&#xff0c;因此围绕高速公路的专用收费 站设计和建设&#xff0c;将有效促进枢纽集散系统与高速公路连通&#xff0c;显著提升城市高速集散能…

ansible-playbook离线升级centos内核

目录 概述实践ansible目录结构关键代码执行效果 结束 概述 内核离线包官网下载地址如下&#xff1a; 地址 实践 ansible目录结构 如对 ansible 不熟悉&#xff0c;离线包下载有问题&#xff0c;请至此地址下载&#xff0c;按本文操作可直接使用。 相关文章链接如下 文章地…