【C++学习手札】模拟实现list

news2024/11/13 10:35:39

                                                      🎬慕斯主页修仙—别有洞天

                                                       ♈️今日夜电波リナリア—まるりとりゅうが

                                                                0:36━━━━━━️💟──────── 3:51
                                                                    🔄   ◀️   ⏸   ▶️    ☰ 

                                      💗关注👍点赞🙌收藏您的每一次鼓励都是对我莫大的支持😍


目录

一、list实际的底层原理

二、list的模拟实现

         写在前面

各层封装的实现

        节点类

        迭代器类 

        list类 

list类详解 

        迭代器实现

list的修改操作

        Insert 

        erase

        swap 

        复用操作(push_back/front、pop_back/front、clear)

list的构造

        建立头节点

        构造函数

        拷贝构造和‘=’重载

        析构函数 

list的空间管理

list的访问相关

三、整体代码 


一、list实际的底层原理

        C++的STL库中的list是使用带头的双向循环链表实现的。list中存储的每个元素包含了两个指针,一个指向前一个节点,一个指向后一个节点,这样就可以方便地在list中进行插入和删除操作,这些操作的时间复杂度都是O(1)。

         大致的结构如下:

         如多大家对于这个数据结构不太熟悉的话,不妨看看作者之前写的一篇关于带头双向循环链表的文章:🌀【数据结构】—C语言实现双向链表(超详细!) 

二、list的模拟实现

         写在前面

        list的底层虽然是带头双向循环链表,但是对于它的实际实现不只是简单的链表而已,当然了,我们肯定会有链表的数据结构。但是,这个“结构”,经过了三层的封装才得以实现,分别是:  第一层:list的节点类

                第二层:list的迭代器类

                第三层:list类

        本文目前只实现正向的迭代器,反向迭代器会在后面的文章统一vector、string、list一起实现。

各层封装的实现

         节点类

        主要是包括:对于两个双向指针的定义以及数据的定义,再是通过初始化列表对于节点的初始化。

    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())//通过初始化列表初始化值
            :_val(val)
            , _pPre(nullptr)
            , _pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };

        迭代器类 

         迭代器实际上是对于指针进行操作,因此我们实例化并且重新命名了节点类的指针PNode,由于迭代器分为是否常量正向迭代器,对此我们额外定义了两个模板参数Ref、Ptr用于控制其中重载运算符 T& operator*() 和 T* operator->()后面的list类中会有体现。在迭代器中,其中,operator*和operator->返回指向节点的指针,operator++和operator--实现前后缀++/--运算符,operator==和operator!=用来比较两个迭代器是否指向同一个节点。

    //List的迭代器类
    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)
        {}
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &*this;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
    private:
        PNode _pNode;
    };

        list类 

        这里先给出主要的封装,具体的实现后面详解。可以看到我们又对于节点类进行了实例化并且重新命名,并且定义了一个数据变量。下面是重点了:我们对于迭代器类也进行了实例化并且重新命名,特别是对于上面我们所提到的Ref和Ptr是有做变动的注意看:对于是否常量正向迭代器分别做出了如下定义:T&, T*和const T&, const T*。这里所这的一些变动也是为了后续简化代码,避免我们因为动静态多一份代码

        请结合上面我们迭代器类中我们所提到的operator==和operator!=理解。

class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;//普通迭代器重命名
        typedef ListIterator<T, const T&, const T*> const_iterator;//静态迭代器重命名
    public:
    //...
    private:
        PNode _pHead;
    };

list类详解 

        在C++中我们通常会进行各类函数的复用,以减少代码量和增加可读性。对此,我们尽量做到复用。

         迭代器实现

        返回头以及尾的迭代器,注意区分动静态 。

        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin()const
        {
            return const_iterator(_pHead->_pNext);

        }
        const_iterator end()const
        {
            return const_iterator(_pHead);
        }

 list的修改操作

        Insert 

         实现在pos位置前插入值为val的节点,开辟空间->保存原位置节点->新节点的前指针指向原节点的前一个节点->后节点指向原节点->该边原节点的前一个节点的后指针指向,指向新节点->原节点的前指针指向新节点->返回性节点的迭代器。

// 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode pNewNode = new Node(val);
            PNode pCur = pos._pNode;

            pNewNode->_pPre = pCur->_pPre;
            pNewNode->_pNext = pCur;
            pNewNode->_pPre->_pNext = pNewNode;
            pCur->_pPre = pNewNode;
            return iterator(pNewNode);
        }

         erase

         删除pos位置的节点,返回该节点的下一个位置,保存删除j节点,保存原节点的下一个节点(用于返回)->一些列删除解链接操作->返回原节点的下一个节点的迭代器

        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            PNode pDel = pos._pNode;
            PNode pRet = pDel->_pNext;

            pDel->_pPre->_pNext = pDel->_pNext;
            pDel->_pNext->_pPre = pDel->_pPre;
            delete pDel;
            return iterator(pRet);
        }

        swap 

        void swap(list<T>& l)
        {
            pNode tmp = _pHead;
            _pHead = l._pHead;
            l._pHead = tmp;
        }

         复用操作(push_back/front、pop_back/front、clear)

        void push_back(const T& val)
        { 
            insert(end(), val);
        }
        void pop_back()
        { 
            erase(--end()); 
        }
        void push_front(const T& val) 
        { 
            insert(begin(), val); 
        }
        void pop_front() 
        { 
            erase(begin());
        }
        void clear()
        {
            iterator p = begin();
            while (p != end())
            {
                p = erase(p);
            }

            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }

 list的构造

         建立头节点

        因为我们在构造、 拷贝等很多的场景中都会用到对于头结点的初始化,对此额外写一个函数用于减少代码量。

        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }

         构造函数

        实现无参、含参初始化、迭代器构造。

        // List的构造
        list()
        {
            CreateHead();
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
            {
                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();
            list<T> temp(l.begin(), l.end());
            this->swap(temp);
        }
        list<T>& operator=(const list<T> l)
        {
            this->swap(l);
            return *this;
        }

        析构函数 

        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }

 list的空间管理

        size_t size()const
        {
            size_t size = 0;
            ListNode* p = _pHead->_pNext;
            while (p != _pHead)
            {
                size++;
                p = p->_pNext;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }

list的访问相关

        主要还是要区分是否为动静态相关。

        T& front()
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }

三、整体代码 

#pragma once
#include<iostream>
#include<assert.h>

using namespace std;


namespace lt
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T())//通过初始化列表初始化值
            :_val(val)
            , _pPre(nullptr)
            , _pNext(nullptr)
        {}
        ListNode<T>* _pPre;
        ListNode<T>* _pNext;
        T _val;
    };


    //List的迭代器类
    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)
        {}
        T& operator*()
        {
            return _pNode->_val;
        }
        T* operator->()
        {
            return &*this;
        }
        Self& operator++()
        {
            _pNode = _pNode->_pNext;
            return *this;
        }
        Self operator++(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pNext;
            return temp;
        }
        Self& operator--()
        {
            _pNode = _pNode->_pPre;
            return *this;
        }
        Self& operator--(int)
        {
            Self temp(*this);
            _pNode = _pNode->_pPre;
            return temp;
        }
        bool operator!=(const Self& l)
        {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l)
        {
            return !(*this != l);
        }
    private:
        PNode _pNode;
    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;//普通迭代器重命名
        typedef ListIterator<T, const T&, const T*> const_iterator;//静态迭代器重命名
    public:
        ///
        // List的构造
        list()
        {
            CreateHead();
        }
        list(int n, const T& value = T())
        {
            CreateHead();
            for (int i = 0; i < n; ++i)
            {
                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();
            list<T> temp(l.begin(), l.end());
            this->swap(temp);
        }
        list<T>& operator=(const list<T> l)
        {
            this->swap(l);
            return *this;
        }
        ~list()
        {
            clear();
            delete _pHead;
            _pHead = nullptr;
        }


        ///
        // List Iterator
        iterator begin()
        {
            return iterator(_pHead->_pNext);
        }
        iterator end()
        {
            return iterator(_pHead);
        }
        const_iterator begin()const
        {
            return const_iterator(_pHead->_pNext);

        }
        const_iterator end()const
        {
            return const_iterator(_pHead);
        }


        ///
        // List Capacity
        size_t size()const
        {
            size_t size = 0;
            ListNode* p = _pHead->_pNext;
            while (p != _pHead)
            {
                size++;
                p = p->_pNext;
            }
            return size;
        }
        bool empty()const
        {
            return size() == 0;
        }


        
        // List Access
        T& front()
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        const T& front()const
        {
            assert(!empty());
            return _pHead->_pNext->_val;
        }
        T& back()
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }
        const T& back()const
        {
            assert(!empty());
            return _pHead->_pPre->_val;
        }


        
        // List Modify
        void push_back(const T& val)
        { 
            insert(end(), val);
        }
        void pop_back()
        { 
            erase(--end()); 
        }
        void push_front(const T& val) 
        { 
            insert(begin(), val); 
        }
        void pop_front() 
        { 
            erase(begin());
        }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val)
        {
            PNode pNewNode = new Node(val);
            PNode pCur = pos._pNode;

            pNewNode->_pPre = pCur->_pPre;
            pNewNode->_pNext = pCur;
            pNewNode->_pPre->_pNext = pNewNode;
            pCur->_pPre = pNewNode;
            return iterator(pNewNode);
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos)
        {
            PNode pDel = pos._pNode;
            PNode pRet = pDel->_pNext;

            pDel->_pPre->_pNext = pDel->_pNext;
            pDel->_pNext->_pPre = pDel->_pPre;
            delete pDel;
            return iterator(pRet);
        }
        void clear()
        {
            iterator p = begin();
            while (p != end())
            {
                p = erase(p);
            }

            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        void swap(list<T>& l)
        {
            pNode tmp = _pHead;
            _pHead = l._pHead;
            l._pHead = tmp;
        }
    private:
        void CreateHead()
        {
            _pHead = new Node;
            _pHead->_pPre = _pHead;
            _pHead->_pNext = _pHead;
        }
        PNode _pHead;
    };
};


                        感谢你耐心的看到这里ღ( ´・ᴗ・` )比心,如有哪里有错误请踢一脚作者o(╥﹏╥)o! 

                                       

                                                                         给个三连再走嘛~  

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

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

相关文章

深入理解计算机系统(原书第三版)PDF 高清中文版

深入理解计算机系统 PDF 深入理解计算机系统 pdf&#xff0c; 这本书的全名是&#xff1a;Computer Systems&#xff1a;A Programmer’s Perspective&#xff08;所以它又被称为 CSAPP&#xff09;&#xff0c;个人习惯把它翻译为程序员所需了解的计算机系统知识&#xff0c;尽…

基于GPRS的汽车碰撞自动报警系统(论文+源码)

1. 系统设计 本次基于GPRS的汽车碰撞自动报警系统的设计中&#xff0c;其主要的目标功能如下&#xff1a;1、实时检测当前的GPS精度和纬度坐标&#xff1b;2.当发生碰撞后系统自动将当前的信息通过GPRS数据发送到远端数据进行报警&#xff1b;3、系统在碰撞后一方面进行本地报警…

运放如何进行全波整流

对于一个双极性的交流信号&#xff0c;如果想要把负半轴的信号镜像到正半轴&#xff0c;我们可以接一个整流桥&#xff0c;这种叫做全波整流。 如果双极性的交流信号经过一个二极管&#xff0c;则交流信号的负半轴不能通过二极管&#xff0c;输出只有正半轴的信号&#xff0c;这…

【UE5】五大基类及其使用

UObject UObject表示对象&#xff0c;准确来说&#xff0c;虚幻引擎中的对象基础类为UObject UObject提供了以下功能&#xff1a; 垃圾收集&#xff08;Garbage collection&#xff09;引用自动更新&#xff08;Reference updating&#xff09;反射&#xff08;Reflection&am…

【面试送分题!“商品分类浏览”如何测试?】

电商项目无论是工作中&#xff0c;还是面试中&#xff0c;都是一个高频出现的词。 面试官非常热衷提问关于电商项目的问题。例如商品分类怎么测试&#xff1f;购物车怎么测试&#xff1f;订单怎么测试&#xff1f;优惠券怎么测试&#xff1f;支付怎么测试&#xff1f;等等。 …

多功能回馈式交流电子负载的应用

多功能回馈式交流电子负载是用于模拟和测试电源、电池等电子设备的负载工具。它具有多种应用&#xff0c;可以用于测试和评估各种类型的电源&#xff0c;包括直流电源和交流电源。它可以模拟各种负载条件&#xff0c;如恒定电流、恒定电压和恒定功率&#xff0c;以验证电源的性…

ubuntu22.04 git 安装

安装git&#xff1a;默认情况下&#xff0c;Git 在 ubuntu 22.04 基础存储库中可用。 现在运行以下命令在您的 Ubuntu 系统上安装最新版本的 Git&#xff1a; 查看当前版本号 git --version

第五天 用Python批量处理Excel文件,实现自动化办公

用Python批量处理Excel文件&#xff0c;实现自动化办公 一、具体需求 有以下N个表&#xff0c;每个表的结构一样&#xff0c;如下&#xff1a; 需要把所有表数据汇总&#xff0c;把每个人的得分、积分分别加起来&#xff0c;然后按总积分排名&#xff0c;总积分一致时&#xff…

leedcode 刷题 - 除自身以外数组的乘积 - 和为 K 的子数组

I238. 除自身以外数组的乘积 - 力扣&#xff08;LeetCode&#xff09; 给你一个整数数组 nums&#xff0c;返回 数组 answer &#xff0c;其中 answer[i] 等于 nums 中除 nums[i] 之外其余各元素的乘积 。 题目数据 保证 数组 nums之中任意元素的全部前缀元素和后缀的乘积都在…

化学气相沉积(CVD)中的TEOS

在半导体制程中&#xff0c;薄膜的沉积是核心的步骤之一&#xff0c;有接触过CVD的小伙伴应该或多或少听过TEOS这种物质&#xff0c;TEOS作为一种重要的沉积源&#xff0c;尤其在低温氧化硅的生成过程中&#xff0c;发挥了无可替代的角色。今天我们就来聊聊这种物质。 什么是TE…

java SpringCloud版本b2b2c鸿鹄云商平台全套解决方案 小程序商城免费搭建

使用技术&#xff1a; Spring CloudSpring BootMybatis微服务服务监控可视化运营 B2B2C平台&#xff1a; 平台管理端(包含自营) 商家平台端(多商户入驻) PC买家端、手机wap/公众号买家端 微服务&#xff08;30个通用微服务如&#xff1a;商品、订单、购物车、个人中心、支…

Microsoft Office 2019下载工具

今天博主继续推出重磅福利——Microsoft Office合集的安装工具。 Microsoft Office是一套由微软公司开发的办公软件&#xff0c;它为 Microsoft Windows 和 Mac OS X而开发。与办公室应用程序一样&#xff0c;它包括联合的服务器和基于互联网的服务。最近版本的 Office 被称为 …

【MySQL】mysql中不推荐使用uuid或者雪花id作为主键的原因以及差异化对比

文章目录 前言什么是UUID?什么是雪花ID?什么是MySql自增ID?优缺点对比UUID:优点1.全球唯一性2.无需数据库支持 缺点1.存储空间大2.索引效率低3.查询效率低 雪花ID&#xff1a;优点1.分布式环境下唯一性 缺点1.依赖于机器时钟2.存储空间较大3.查询效率低 MYSQL自增:优点1.简单…

聚类算法模型的概念、评估及应用

聚类是一种无监督学习方法&#xff0c;其目标是将数据集中的样本分成不同的组别&#xff0c;使得同一组内的样本相似度较高&#xff0c;而不同组之间的样本相似度较低。聚类算法模型通常通过计算样本之间的相似度或距离来实现这一目标。以下是聚类算法模型的概念、评估及应用的…

电脑技巧:推荐八个非常实用的在线网站值得收藏

目录 1、wikihow 干货分享网站 2、次元小镇 二次元必备网站 3、AI创作家 4、SKRbt 搜索引擎网站 5、barbg 全球资源网站 6、书签地球 7、4KHDR世界 8、a real me 今天小编给大家推荐八个非常实用的在线网站值得收藏&#xff01; 1、wikihow 干货分享网站 这个网站是一…

文章解读与仿真程序复现思路——电工技术学报EI\CSCD\北大核心《面向差异化电源成本结构的容量市场机制设计》

这个文章标题涉及到容量市场机制设计&#xff0c;着重考虑了电源成本结构的差异性。下面对标题中的关键词进行解读&#xff1a; 面向&#xff08;Facing&#xff09;&#xff1a; 表示该容量市场机制设计是以某种方向、取向或目标为基础的。在这里&#xff0c;可能指的是设计是…

九州未来联合联通智网科技发布白皮书,促进车联网融合发展

2023年11月21日&#xff0c;由2023中国5G工业互联网大会组委会、工业和信息化部主办&#xff0c;联通智网科技承办的2023中国5G工业互联网大会——5G车联网与智慧交通创新发展平行会议&#xff0c;在武汉成功举办。 九州未来作为中国联通车联网创新联合体成员单位&#xff0c;受…

自学编程,用好这几个网站就够了!

如果你要自学编程&#xff0c;一定要收藏好这7个网站&#xff0c;上面免费的优质教程很多&#xff0c;完全可以省去你上万块钱的学费&#xff01; 话不多说&#xff0c;直接上干货&#xff01; 第一个&#xff0c;W3school 一个主打图文教程的网站&#xff0c;不管是前端开发…

域控操作五:统一熄屏睡眠时间

直接看图路径&#xff0c;我只设置了熄屏&#xff0c;如果要睡眠就下面那个启用设置时间

本地部署 ComfyUI

本地部署 ComfyUI ComfyUI 介绍ComfyUI Github 地址部署 ComfyUI配置模型地址 or 下载模型启动 ComfyUI访问 ComfyUI ComfyUI 介绍 最强大、模块化的稳定扩散 GUI 和后端。 该用户界面将允许您使用基于图形/节点/流程图的界面设计和执行高级稳定扩散管道。 ComfyUI Github 地…