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

news2024/11/23 2:50:54

目录

一、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.coloradmin.cn/o/1627306.html

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

相关文章

yolov8 dll 编译

1. 每次用yolo v8 都要用python &#xff0c;对于我这种写软件的太不方便了&#xff0c;下面尝试编译dll 调用, 我已经有做好的模型.best.pt 参考视频方法: yolov8 TensorRT C 部署_哔哩哔哩_bilibili 【yolov8】tensorrt部署保姆级教程&#xff0c;c版_哔哩哔哩_bilibili 需…

C语言基础知识笔记——万字学习记录

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要参考浙大翁恺老师的C语言讲解以及其他博主的C语言学习笔记&#xff0c;进而梳理C语言的基础知识&#xff0c;为后续系统性学习数据结构和其他语言等知识夯实一定的基础。&#xff08;其他博主学习笔记的链接包括&#x…

陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知

陕西工程系列建设工程专业工程师评审工作要求链接陕西省人力资源和社会保障厅 陕西省住房和城乡建设厅 关于开展2023年度全省建设工程专业高级工程师评审工作的通知 - 陕西省住房和城乡建设厅类别基本条件业绩成果备注助理工程师 最新公告http://www.snhrm.com/zxggao2/597358…

怎么排查K8S容器当中的Java程序内存泄露问题

今天早上发现生产线其中的一个服务在凌晨的时候突然重启了&#xff0c;内存突然从1G升到1.8G&#xff0c;CPU使用量从0.1升到了0.28&#xff0c;说明在这个时间点&#xff0c;内存突增达到了限额以上&#xff0c;服务重启了。因为这个服务布署了多节点&#xff0c;这次重启对业…

不同技术实现鼠标滚动图片的放大缩小

摘要&#xff1a; 最近弄PC端的需求时&#xff0c;要求在layui技术下实现鼠标滚动图片的放大缩小的功能&#xff01;下面来总结一下不同框架剩下这功能&#xff01; layui: 看了一下layui文档&#xff0c;其实这有自带的组件的&#xff01;但是又版本要求的!并且layui的官方文档…

element 分页切换时:current-page无效 页数不会跟着一起切换

问题回溯&#xff1a;使用el-pagination组件 选择切换当前分页 页数为2 问题结果&#xff1a;el-pagination组件 当前页切换失败 一直都是 1&#xff0c;接口传参分页数据是2&#xff0c;打印当前分页也是2 解决方案1&#xff1a;使用 current-page参数 .sync 修饰符 解决方案2…

北京车展创新纷呈,移远通信网联赋能

时隔四年&#xff0c;备受瞩目的2024&#xff08;第十八届&#xff09;北京国际汽车展览会于4月25日盛大开幕。在这场汽车行业盛会上&#xff0c;各大主流车企竞相炫技&#xff0c;众多全球首发车、概念车、新能源车在这里汇聚&#xff0c;深刻揭示了汽车产业的最新成果和发展潮…

某赛通电子文档安全管理系统 多处 SQL注入漏洞复现

0x01 产品简介 某赛通电子文档安全管理系统(简称:CDG)是一款电子文档安全加密软件,该系统利用驱动层透明加密技术,通过对电子文档的加密保护,防止内部员工泄密和外部人员非法窃取企业核心重要数据资产,对电子文档进行全生命周期防护,系统具有透明加密、主动加密、智能…

函数模板与类模板初阶

如果要写一个交换函数&#xff0c;不同类型的话调用不同的交换函数&#xff0c;如果使用重载的话只能解决函数名相同但是会根据参数类型调用不同的函数。即使这样也依旧要写很多不同类型的swap交换函数 函数重载的交换函数 仔细观察会发现除了类型不同其他的函数结构什么的都一…

Postman 在 Linux 上的安装指南:简单快速开始使用

本文将介绍如何在 Linux 上安装 Postman 的详细步骤&#xff0c;Postman 支持的 Linux 的发行版包括&#xff1a;Ubuntu 14.04 及更高版本&#xff0c;Fedora 24&#xff0c;Debian 8 及更高版本。下面将介绍其具体的安装方法。 手动安装 Postman 的下载地址&#xff0c;下载…

一、Django 初识

简介 Django 是一个用于构建 Web 应用程序的高级 Python Web 框架。 版本对应 不同版本的django框架是基于特定的不同的python版本开发的&#xff0c;所以不同版本的django框架要正常执行功能只能安装特定的python版本 Django安装 安装 Django # 全局安装 pip install dj…

泰坦尼克号乘客生存情况预测分析2

泰坦尼克号乘客生存情况预测分析1 泰坦尼克号乘客生存情况预测分析2 泰坦尼克号乘客生存情况预测分析3 泰坦尼克号乘客生存情况预测分析总 背景描述 Titanic数据集在数据分析领域是十分经典的数据集&#xff0c;非常适合刚入门的小伙伴进行学习&#xff01; 泰坦尼克号轮船的…

ionic 中对Input输入框、searchbar进行solr检索

一、概述 Ionic 是一个用于开发跨平台应用程序的开源工具&#xff0c;可以使用 Angular、React 或 Vue 等前端框架。要在 Ionic 应用程序中实现实时与 Solr 通信&#xff0c;可以使用 HTTP 客户端&#xff08;如 Angular 的 HttpClient 或 Ionic 的 Native HTTP&#xff09;…

笔记:编写程序,绘制一个展示 2013~2019 财年阿里巴 巴淘宝+天猫平台的 GMV 的柱形图,实现过程如下:

文章目录 前言一、GMV 的柱形图是什么&#xff1f;二、编写代码总结 前言 编写程序。根据实例 2 的要求&#xff0c;绘制一个展示 2013~2019 财年阿里巴 巴淘宝天猫平台的 GMV 的柱形图&#xff0c;实现过程如下&#xff1a; &#xff08;1&#xff09; 导入 matplotlib.pypl…

Linux快速部署大语言模型LLaMa3,Web可视化j交互(Ollama+Open Web UI)

本文在个人博客同步发布&#xff0c;前往阅读 1 介绍 本文将介绍使用开源工具Ollama(60.6k⭐)部署LLaMa大模型&#xff0c;以及使用Open WebUI搭建前端Web交互界面的方法。 我们先来过一遍几个相关的概念&#xff0c;对这块比较熟悉的朋友可跳过。 1.1 大规模语言模型 大规…

从递归角度串联二叉树-图论-动态规划

一、深度理解二叉树的前中后序遍历 二叉树遍历框架如下&#xff1a; void traverse(TreeNode* root) {if (root nullptr) {return;}// 前序位置traverse(root->left);// 中序位置traverse(root->right);// 后序位置 }先不管所谓前中后序&#xff0c;单看 traverse 函数…

keytool,openssl的使用

写在前面 在生成公钥私钥&#xff0c;配置https时经常需要用到keytool&#xff0c;openssl工具&#xff0c;本文就一起看下其是如何使用的。 keytool是jdk自带的工具&#xff0c;不需要额外下载&#xff0c;但openssl需要额外下载 。 1&#xff1a;使用keytool生成jks私钥文件…

Office Word自动编号转文本

原理 使用office自带的宏功能&#xff0c;一键替换 过程 调出word的“开发工具”选项 文件->选项->自定义功能区->选中开发工具->确定 创建宏 开发工具->宏->创建宏 编写宏 在弹出来的框里&#xff0c;替换代码为 Sub num2txt() ActiveDocument.…

ArcGIS批量寻找图层要素中的空洞

空洞指的是图层中被要素包围所形成的没有被要素覆盖的地方&#xff0c;当图层要素数量非常庞大时&#xff0c;寻找这些空洞就不能一个一个的通过目测去寻找了&#xff0c;需要通过使用工具来实现这一目标。 一、【要素转线】工具 利用【要素转线】工具可以将空洞同图层要素处于…

电商技术揭秘三十五:智能风控功能架构浅析

相关系列文章 电商技术揭秘相关系列文章合集&#xff08;1&#xff09; 电商技术揭秘相关系列文章合集&#xff08;2&#xff09; 电商技术揭秘二十八&#xff1a;安全与合规性保障 电商技术揭秘二十九&#xff1a;电商法律合规浅析 电商技术揭秘三十&#xff1a;知识产权保…