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.mfbz.cn/a/576410.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

软件设计师-重点的创建型设计模式

一、简单工厂&#xff1a; 简单工厂模式属于创建型模式&#xff0c;但不属于23种设计模式之一。 软考中图 二、工厂方法&#xff1a; 意图&#xff1a; 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类。Factory Method 使一个类的实例化延迟到其子类。 结…

光度立体法估计法线与反射率重建场景

1 从明暗恢复形状 从明暗恢复形状&#xff08;Shape from Shading&#xff0c;SfS&#xff09;是指从图像的明暗信息推断出物体表面几何形状的过程。这个问题假设光照条件已知&#xff0c;目标表面是光滑且均匀的&#xff0c;并且照明是单向的。其基本思想是根据目标表面对光照…

计算机组成原理实验(一)--可控加减法电路设计实验

一、一位全加器的设计 视频学习链接&#xff1a;3-2-4 定点数的加法和减法运算 — 一位全加器的硬件逻辑实现_哔哩哔哩_bilibili 仿真电路图&#xff1a; 总结&#xff1a;奇数个1时Si输出为1&#xff0c;偶数个1输出为0&#xff1b;1的个数大于等于2时&#xff0c;Ci输出1 实…

Kafka 3.x.x 入门到精通(05)——对标尚硅谷Kafka教程

Kafka 3.x.x 入门到精通&#xff08;05&#xff09;——对标尚硅谷Kafka教程 2. Kafka基础2.1 集群部署2.2 集群启动2.3 创建主题2.4 生产消息2.5 存储消息2.6 消费消息2.6.1 消费消息的基本步骤2.6.2 消费消息的基本代码2.6.3 消费消息的基本原理2.6.3.1消费者组2.6.3.1.1 消费…

【优秀AI项目】每日跟踪 OpenVoice ,AI快站,OpenVoice

持续更新好玩的开源AI项目或AI商业应用体验 一起来玩转AI&#xff01;&#xff01; 1 huggingface 国内镜像站&#xff1a;AI 快站 HUggingface被墙了&#xff0c;emmmmm 所以我之前玩模型的一大感觉就是 下载什么模型之类的太难受了&#xff01;服了 看到一个镜像站——…

如何使用bof-launcher在CC++Zig应用程序中执行Beacon对象文件(BOF)

关于bof-launcher bof-launcher是一款针对Beacon对象文件&#xff08;BOF&#xff09;的安全测试工具&#xff0c;在该工具的帮助下&#xff0c;广大研究人员可以轻松在C/C/Zig应用程序中执行Beacon对象文件&#xff08;BOF&#xff09;。 Cobalt Strike 4.1于2020年6月25日发…

[Diffusion Model 笔记]DDIM 笔记 数学推导 Denoising Diffusion Implicit Models

目录 核心总结符号定义第一套&#xff0c;快速简单讲清采样方法继续分析&#xff0c;待定系数法求解图示理解关于参数sigma 本文是观看以下视频的笔记&#xff0c;强烈推荐观看最后的图示理解&#xff1a; https://www.bilibili.com/video/BV13P411J7dm/?spm_id_from333.788 论…

数据结构|树形结构|并查集

数据结构|并查集 并查集 心有猛虎&#xff0c;细嗅蔷薇。你好朋友&#xff0c;这里是锅巴的C\C学习笔记&#xff0c;常言道&#xff0c;不积跬步无以至千里&#xff0c;希望有朝一日我们积累的滴水可以击穿顽石。 有趣的并查集剧情演绎&#xff1a;【算法与数据结构】—— 并…

idea自定义配置文件的注释

打开 IntelliJ Idea 软件 依次找到 File—>Editor—>File and Code Templates 设置 Files 下的Class、Interface、Enum等 输入下面的内容 /** * description: ${NAME} * date: ${YEAR}-${MONTH}-${DAY} ${HOUR}:${MINUTE} * author: author **/

php动态高亮web源代码

php动态高亮web源代码 注&#xff1a;配置好不允许高亮的文件名&#xff0c;安全第一 #php实现动态展示目录树结构源代码 适用于开放源代码&#xff0c;结合html缓存使用效果更佳&#xff0c;因循环较多不适合放首页 能力有限没实现行号 演示&#xff1a;show source|开放…

吉布提国家概况

吉布提国家概况 &#xff08;最近更新时间&#xff1a;2022年10月&#xff09; 【国 名】 吉布提共和国&#xff08;The Republic of Djibouti&#xff0c; La Rpublique de Djibouti&#xff09;。 【面 积】 2.32万平方公里。 【人 口】约100万。主要有伊萨族和阿法尔族。…

认识HTTP

HTTP缺点 通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听 不验证通信方的身份&#xff0c;可能遭遇伪装 无法证明报文的完整性&#xff0c;所以有可能遭篡改 一、通信使用明文&#xff08;不加密&#xff09;&#xff0c;内容可能会被窃听 TCP/…

鸿蒙OpenHarmony【轻量系统 编译】 (基于Hi3861开发板)

编译 OpenHarmony支持hb和build.sh两种编译方式。此处介绍hb方式&#xff0c;build.sh脚本编译方式请参考[使用build.sh脚本编译源码]。 使用build.sh脚本编译源码 进入源码根目录&#xff0c;执行如下命令进行版本编译。 ./build.sh --product-name name --ccache 说明&…

【算法基础实验】图论-深度优先搜索和深度优先路径

深度优先(DFS) 理论基础 深度优先搜索&#xff08;DFS, Depth-First Search&#xff09;是图和树的遍历算法中的一种&#xff0c;它从一个节点开始&#xff0c;沿着树的边走到尽可能深的分支&#xff0c;直到节点没有子节点为止&#xff0c;然后回溯继续搜索下一个分支。DFS …

python基础之元组、集合和函数的定义与返回值

1.元祖 1.元祖的定义 元组的数据结构跟列表相似 特征&#xff1a;有序、 有序&#xff1a;有&#xff08;索引/下标/index&#xff09; 正序、反序标识符&#xff1a; ( ) 里面的元素是用英文格式的逗号分割开来关键字&#xff1a;tuple 列表和元组有什么区别&#xff1f; 元组…

JavaEE 初阶篇-深入了解 I/O 高级流(缓冲流、交换流、数据流和序列化流)

&#x1f525;博客主页&#xff1a; 【小扳_-CSDN博客】 ❤感谢大家点赞&#x1f44d;收藏⭐评论✍ 文章目录 1.0 缓冲流概述 1.1 缓冲流的工作原理 1.2 使用缓冲流的步骤 1.3 字节缓冲流于字符缓冲流的区别 1.4 字节缓冲流的实例 1.5 字符缓冲流的实例 2.0 转换流概述 2.1 字符…

局部多项式近似与 AMPM 算法

kappa3; %已在您的代码中定义% 定义窗口大小 windowSize (2*kappa1);% 初始化梯度估计值 [rows, cols] size(wrappedPhase); phi_y zeros(rows, cols); phi_x zeros(rows, cols);% 遍历每个窗口 for m 1kappa:rows-kappafor n 1kappa:cols-kappa% 提取局部窗口Z_mn wrap…

Git--基础学习--面向企业--持续更新

一、基础学习 1.1基本命令 //查询基础信息 git config --global --list //选取合适位置创建 mkdir 文件名 //创建文件夹 //全局配置 git config --global user.email "****e***i" git config --global user.name "*** K****"//--------------------进入…

SHViT:具有内存高效宏设计的单头视觉Transformer

文章目录 摘要1、引言2、分析与方法2.1、宏观设计中的冗余分析2.2、微观设计中的冗余分析2.3、单头自注意力2.4、单头视觉转换器 3、实验3.1、实现细节3.2、SHViT在ImageNet-1K分类任务上的表现3.3、SHViT在下游任务中的表现3.4、消融研究 4、相关工作5、结论SHViT&#xff1a;…

python-opencv实现最近邻插值和双线性插值对图片上采样

使用背景 当我们需要把图像进行放大或者缩小的时候&#xff0c;第一反应是使用resize()实现。很多情况下&#xff0c;我们会调用最近邻插值和双线性插值去放大图片&#xff0c;当然要说没有分辨率的损失那是不可能的&#xff0c;只能说在放大图片的过程中尽可能增加了图片的分…