【c++】STL--List的实现

   

     

目录

一.  List的数据结构

二.  List实现的基本框架

1. list的结点结构类      

 2. List的迭代器类

正向迭代器

反向迭代器

3. List操作接口的实现

 1. 默认成员函数

构造函数 和 析构函数   

拷贝构造函数 和 赋值运算符重载

2. 修改相关函数接口 

insert 和 erase

push_back 和 push_front

pop_front 和 pop_back

 3. 迭代器相关接口

begin() 和 end()

rbegin() 和 rend()

  4. 其他相关函数接口

      swap                  

      clear

      Head_init 

三.  完整源代码实现



                      

一.  List的数据结构

     list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代,其底层是带头双向循环链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向 其前一个元素和后一个元素,list实现上就是一个双向循环链表,list节点 有prev 和 next 两个指针。        

     使用链表存储数据,并不会将它们存储到一整块连续的内存空间中。恰恰相反,各元素占用的存储空间(又称为节点)是独立的、分散的,它们之间的线性关系通过指针来维持的

二.  List实现的基本框架

   List的实现需要实现三个类 (类模板形式)    

         list的结点结构类

         List的迭代器类

         List功能的实现类

1. list的结点结构类      

      对节点的定义如下

    //list结点类模板template <class T>struct listnode{listnode* prev;listnode* next;T data;//结点的构造函数,完成对节点的初始化listnode(const T& val = T()):prev(nullptr),next(nullptr),data(val){}};

     可以看到,list 容器定义的每个节点中,都包含 *prev、*next 和 data。其中,prev 指针用于指向前一个节点;next 指针用于指向后一个节点;data用于存储当前元素的值。

 2. List的迭代器类

List 的迭代器迭代器有两种实现方式,具体应根据容器底层数据结构实现:

    1. 原生态指针,比如:vector

    2. 将原生态指针进行封装,因迭代器使用形式与指针完全相同,因此在自定义的类中须实现以下方法

           1. 指针可以解引用,迭代器的类中必须重载operator*()

           2. 指针可以通过->访问其所指空间成员,迭代器类中必须重载oprator->()

           3. 指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)至于  operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前移动,所以需要重载,如果是forward_list就不需要重载--

           4. 迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()

        和 vector 容器迭代器的实现方式不同,由于 list 容器的元素并不是连续存储的,所以该容器迭代器中,必须包含一个可以指向 list 容器的指针,并且该指针还可以借助重载的 *、++、--、->、==、!= 等运算符,实现迭代器正确的递增、递减、取值等操作,以达到 vector 迭代器的同等效果

      

正向迭代器

   list 容器正向迭代器的实现代码如下:    

    //list正向迭代器类template <class T, class Ref, class Ptr>struct _list_iterator{typedef listnode<T> Node; //在迭代器中实例化结点模板typedef _list_iterator<T, Ref, Ptr> self; //对迭代器的类型重命名,已达简化//构造_list_iterator(Node* node = nullptr):_node(node){}// 迭代器支持移动// 前置++self& operator++(){_node = _node->next;return *this;}//后置++self operator++(int){self tmp(*this);++_node;return tmp;}//前置--self& operator--(){_node = _node->prev;return *this;}//后置--self operator--(int){self tmp(*this);--_node;return tmp;}// 具有指针类似行为Ref operator*(){return _node->data;}//当结点数据类型仍然为自定义结构时Ptr operator->(){return &_node->data;}// 迭代器支持比较bool operator!=(const self& s) const{return _node != s._node;}bool operator==(const self& s) const{return _node == s._node;}Node* _node;};

反向迭代器

  list 容器反向迭代器的实现代码如下

    //list反向迭代器类template <class Iterator, class Ref, class Ptr>struct _list_reverse_iterator{typedef _list_reverse_iterator<Iterator, Ref, Ptr> self;//构造_list_reverse_iterator(Iterator it):_cur(it){}// 迭代器支持移动//前置++self& operator++(){--_cur;return *this;}//后置++self operator++(int){self tmp(*this);--_cur;return tmp;}//前置--self& operator--(){++_cur;return *this;}//后置--self operator--(int){self tmp(*this);++_cur;return tmp;}// 具有指针类似行为Ref operator*(){Iterator tmp = _cur;return *--tmp;}Ptr operator->(){return &(*_cur);}// 迭代器支持比较bool operator!=(const self& s) const{return _cur != s._cur;}bool operator==(const self& s) const{return _cur == s._cur;}Iterator _cur;};

3. List操作接口的实现

 1. 默认成员函数

     构造函数 和 析构函数   

 以下,创建了空的 list 容器(即无参的构造函数),但它也包含有 1 个节点。

    除此之外,list 模板类中还提供有带参的构造函数,它们的实现过程大致分为以下 2 步:

            1. 调用 Head_init() 函数,构造带有头节点的空 list 容器链表;     

             2. 将各个参数按照次序插入到空的 list 容器链表中。

		//构造函数list(){Head_init(); //初始化头结点}//构造n个值为val的节点链表的构造函数list(int n, const T& val = T()){Head_init(); //对链表头结点初始化while (n--)  //依次尾插n各值为val的结点{push_back(val);}}//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last){Head_init();while (first != last){push_back(*first);++first;}}//析构函数~list(){clear(); //清空链表结点delete _head; //释放头结点_head = nullptr; //头结点置空}

                       

拷贝构造函数 和 赋值运算符重载
        //拷贝构造list(const list<T>& lt){Head_init(); //初始化for (const auto& e : lt){push_back(e);}}//赋值运算符重载list<T>& operator=(list<T> lt){swap(lt); //交换两者数据,交换两者头结点的指针即可return *this;}

2. 修改相关函数接口 

     insert 和 erase

   insert : 通过在指定位置的元素之前插入新元素(插入位置是任意的)                       

        //在pos位置插入一个结点iterator insert(iterator pos, const T& val){Node* newnode = new Node(val); //申请一个新节点Node* curnode = pos._node;  //pos位置的结点newnode->prev = curnode->prev; //先与当前结点的前一个结点链接上curnode->prev->next = newnode;newnode->next = curnode; //在与当前结点链接curnode->prev = newnode;return newnode; //返回新插入结点的迭代器}

说明:此在list中进行插入时是不会导致list的迭代 器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响

注意:节点的连接顺序,先连后断

erase: 删除所指向的位置的结点删除位置是任意的)

        //删除pos位置的结点iterator erase(iterator pos){assert(pos != end());Node* curnode = pos._node; //保存要删除的结点Node* nextnode = curnode->next; //删除结点的下一结点curnode->prev->next = nextnode;nextnode->prev = curnode->prev;delete curnode;return nextnode; //返回删除结点的后一个结点的迭代器}

说明:list 在删除一个结点时会存在迭代器失效,(即被删除的那个位置的迭代器失效),

push_back 和 push_front

尾插

在end()迭代器所在的位置的前一个结点之后插入新的结点

        //尾插void push_back(const T& val){/*Node* newnode = new Node(val);Node* tail = _head->prev;tail->next = newnode;newnode->prev = tail;newnode->next = _head;_head->prev = newnode;*///复用insertinsert(end(), val);}

在end()位置之前插入一个结点(即在尾部插入结点)

                         

头插

在begin()迭代器所在的位置之前插入新的结点

        //头插void push_front(const T& val){insert(begin(), val); //复用insert}

pop_front 和 pop_back

头删

删除begin()迭代器所在的位置的结点

        //头删void pop_front(){erase(begin()); //复用erase}

尾删

删除end()迭代器所在的位置的前一个结点

        //尾删void pop_back(){erase(--end()); }

 3. 迭代器相关接口

     begin() 和 end()
        iterator begin(){return iterator(_head->next);//构造匿名对象//return _head->next; //隐式类型转换}iterator end(){return iterator(_head);//return _head; }//const的迭代器const_iterator begin() const{return const_iterator(_head->next);//return _head->next;}const_iterator end() const{return const_iterator(_head);//return _head;}

rbegin() 和 rend()
        reverse_iterator rbegin() {return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}//const的反向迭代器const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}

  4. 其他相关函数接口

         swap                  

    用于交换两对象的数据的内容,实际改变头结点的指针的指向           

        void swap(list<T>& tmp){std::swap(_head, tmp._head);}
      clear

清空链表结点(头结点除外) 

        void clear(){iterator it = begin();while (it != end()){it = erase(it);}}
      Head_init 

       初始化头结点                    

        //初始化头结点void Head_init(){_head = new Node; //申请一个空白结点(头结点)_head->next = _head; _head->prev = _head;}

三.  完整源代码实现

    //list结点类template <class T>struct listnode{T data;listnode* prev;listnode* next;listnode(const T& val = T()):data(val),prev(nullptr),next(nullptr){}};//list正向迭代器类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* node):_node(node){}//前置++self& operator++(){_node = _node->next;return *this;}//后置++self operator++(int){self tmp(*this);++_node;return tmp;}//前置--self& operator--(){_node = _node->prev;return *this;}//后置--self operator--(int){self tmp(*this);--_node;return tmp;}Ref operator*(){return _node->data;}Ptr operator->(){return &_node->data;}bool operator!=(const self& s) {return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};//list反向迭代器类template <class Iterator, class Ref, class Ptr>struct _list_reverse_iterator{typedef _list_reverse_iterator<Iterator, Ref, Ptr> self;_list_reverse_iterator(Iterator it):_cur(it){}//前置++self& operator++(){--_cur;return *this;}//后置++self operator++(int){self tmp(*this);--_cur;return tmp;}//前置--self& operator--(){++_cur;return *this;}//后置--self operator--(int){self tmp(*this);++_cur;return tmp;}Ref operator*(){Iterator tmp = _cur;return *--tmp;}Ptr operator->(){return &(*_cur);}bool operator!=(const self& s){return _cur != s._cur;}bool operator==(const self& s){return _cur == s._cur;}Iterator _cur;};//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;typedef _list_reverse_iterator<iterator, T&, T*>  reverse_iterator;typedef _list_reverse_iterator<const_iterator, const T&, const T*>  const_reverse_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);}reverse_iterator rbegin() {return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}void Head_init(){_head = new Node;_head->next = _head;_head->prev = _head;}//构造函数list(){Head_init();}list(int n, const T& val = T()){Head_init();while (n--){push_back(val);}}//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last){Head_init();while (first != last){push_back(*first);++first;}}//拷贝构造list(const list<T>& lt){Head_init();for (const auto& e : lt){push_back(e);}}void swap(list<T>& tmp){std::swap(_head, tmp._head);}//赋值运算符重载list<T>& operator=(list<T> lt){swap(lt);return *this;}//析构~list(){clear();delete _head;_head = nullptr;}//在pos位置插入一个结点iterator insert(iterator pos, const T& val){Node* newnode = new Node(val);Node* curnode = pos._node;newnode->prev = curnode->prev;curnode->prev->next = newnode;newnode->next = curnode;curnode->prev = newnode;return newnode;}//删除pos位置的结点iterator erase(iterator pos){assert(pos != end());Node* curnode = pos._node;Node* nextnode = curnode->next;curnode->prev->next = nextnode;nextnode->prev = curnode->prev;delete curnode;return nextnode;}//尾插void push_back(const T& val){/*Node* newnode = new Node(val);Node* tail = _head->prev;tail->next = newnode;newnode->prev = tail;newnode->next = _head;_head->prev = newnode;*/insert(end(), val);}//头插void push_front(const T& val){insert(begin(), val);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}private:Node* _head;};

                   


                      

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

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

相关文章

thinkphp实现对两个字段或or条件搜索

thinkphp实现对两个字段或or条件搜索 $mD(Adstext);$data[adstext_title]array(like,"%{$keyword}%");$data[id]array(like,"%{$keyword}%");$data[_logic] or; //条件或$arr$m->where($data)->relation(true)->select(); // du…

R语言简介、环境与基础语法及注释

R语言简介、环境与基础语法及注释 一、R语言1.R语言简介2.R语言官网3.R语言中国的镜像网站4.R语言下载5.R语言的历史 二、R语言环境1.Windows安装1.1 去 R 语言下载的镜像站点的列表下载1.2 选择版本进行下载1.3 点击运行1.4 一路默认&#xff0c;安装完毕&#xff01; 2.Linux…

【AI视野·今日Robot 机器人论文速览 第八十期】Fri, 1 Mar 2024

AI视野今日CS.Robotics 机器人学论文速览 Fri, 1 Mar 2024 Totally 32 papers &#x1f449;上期速览✈更多精彩请移步主页 Daily Robotics Papers Humanoid Locomotion as Next Token Prediction Authors Ilija Radosavovic, Bike Zhang, Baifeng Shi, Jathushan Rajasegaran…

CCF会议期刊(计算机网络)

中国计算机学会推荐国际学术会议 序号会议简称会议全称分类类型专业领域1SIGCOMMACM International Conference on Applications, Technologies, Architectures, and Protocols for Computer CommunicationA会议计算机网络2MobiComACM International Conference on Mobile Com…

【笔记】MTK与高通平台emergency APN配置差异

协议规定 根据3GPP协议&#xff0c;emergency APN配置需要注意。 3GPPspec TS24.301 6.5.1.2 UE requested PDN connectivity procedure initiation In order to request a PDN connection for emergency bearer services, the UE shall not include an APN in the PDN CONNE…

❤ 前端实现发邮件

❤ 前端实现发邮件 1、Nodejs使用nodemailer发邮件 一、开启发送者邮箱的SMTP服务 QQ邮箱开启SMTP服务教程 163邮箱开启SMTP服务教程 二、安装nodemailer模块 在node项目目录下&#xff0c;使用cmd运行下面命令 npm install nodemailer --savenodemailer官网 https://nod…

ShardingSphere-SQL 解析 Issue 处理流程

ShardingSphere-SQL 解析 Issue 处理流程 这是之前给社区写的 SQL 解析 Issue 的处理流程&#xff0c;可以帮助社区用户快速参与到 ShardingSphere-SQL 解析任务当中。 ShardingSphere SQL 解析 issue 列表 Issue 背景说明 当前 Issue 使用自定义的爬虫脚本从对应的数据库官…

MySQL-----视图

一 视图 ▶ 介绍 视图view是一个虚拟表&#xff0c;非真实存在&#xff0c;其本质是根据SQL语句获取动态的数据集&#xff0c;并为其命名&#xff0c;用户使用时只需使用视图名称即可获取结果集&#xff0c;并可以将其当作表来使用。 数据库中存放了视图的定义&…

Java程序员修炼之道 之 Logging

1. 一个最基本的例子 使用Logging框架写Log基本上就三个步骤 引入loggerg类和logger工厂类 声明logger 记录日志 下面看一个例子 //1. 引入slf4j接口的Logger和LoggerFactory import org.slf4j.Logger; import org.slf4j.LoggerFactory; public class UserService { //…

轻量级Redis慢查询监控脚本

Redis是一款非常强大的内存数据库&#xff0c;可以用于缓存、事务、队列等多种场景。但是在使用Redis的过程中&#xff0c;可能会遇到慢查询的问题。为了提高Redis的性能和响应速度&#xff0c;我们需要了解和处理慢查询。本文将围绕分享Redis慢查询脚本这一主题&#xff0c;讲…

ts快速上手笔记02

第二章&#xff1a;面向对象 面向对象是程序中一个非常重要的思想&#xff0c;它被很多同学理解成了一个比较难&#xff0c;比较深奥的问题&#xff0c;其实不然。面向对象很简单&#xff0c;简而言之就是程序之中所有的操作都需要通过对象来完成。 举例来说&#xff1a; 操作…

C#封装常用的Redis工具类

1.请先安装CSRedisCore 接口&#xff1a; namespace Tools.Redis {public interface IRedisTool{bool SetLongValue(string key, string value);bool SetValue(string key, string value, int outSecond);bool SetValue(string key, string value);bool Exists(string key);b…

揭开ChatGPT的智能对话奥秘——深度剖析其工作原理与关键技术

导语 让我们携手走进一个极具革新性的科技前沿领域&#xff0c;一同揭示能够与人类实现自然流畅对话的ChatGPT背后的运作机制。它犹如一个拥有无尽智慧的对话伙伴&#xff0c;跨越知识边界&#xff0c;回应各种疑问&#xff0c;并能创作出精辟的文章和诗篇。那么&#xff0c;C…

TDengine Schemaless常见问题的原因及故障排除

Tips&#xff1a;使用版本&#xff1a;3.0.2.6 &#xff08;一&#xff09;TDengine ERROR (80003002): Invalid data format 格式化问题&#xff1b;如缺少必要的组成格式&#xff08;时间戳、超级表等&#xff09;&#xff0c;或有字符串未作修饰符修饰&#xff0c;类似的还…

js和css阻塞问题

面试常见问题 css 加载会不会阻塞 js 的加载&#xff1f;&#xff08;不会&#xff09;css 加载会不会阻塞 js 的执行&#xff1f;&#xff08;会&#xff09;css 加载会不会阻塞 DOM 的解析&#xff1f;&#xff08;不会&#xff09;css 加载会不会阻塞 DOM 的渲染&#xff1…

解析Excel数据如虎添翼:Excel数据监听器助你快速解析数据,轻松驾驭业务需求,一键解析,风云再起,数据处理从未如此简单,事半功倍

以下代码是一个Excel数据监听器&#xff0c;用于监听和处理Excel数据的读取事件。它实现了AnalysisEventListener接口&#xff0c;并重写了其中的方法。以下是代码中的主要部分&#xff1a; invokeHead方法&#xff1a;在解析Excel表格的表头时触发的回调方法。通过比较模板表…

企业数字人虚拟形象定制解决方案

随着数字化浪潮的推进&#xff0c;虚拟形象在各个领域都展现出了强大的潜力&#xff0c;美摄科技作为业界领先的数字人虚拟形象定制解决方案提供商&#xff0c;致力于为企业打造独一无二的虚拟形象&#xff0c;助力企业在数字世界中塑造独特的品牌形象。 一、解决方案概览 美…

理德外汇名人故事:个人投资者大使——威廉·欧奈尔

威廉•欧奈尔&#xff08;William J.O’Neil&#xff09;是美国著名的成长性企业的投资大师&#xff0c;投资生涯接近50年。1988年&#xff0c;出版《How to Make Money in Stocks: A winning system in good times or bad》一书&#xff0c;国内译为《笑傲股市》&#xff0c;成…

02-prometheus监控-服务器节点监控node-exporter

一、概述 prometheus&#xff0c;本身是一个【数据收集】和【数据处理】的工具&#xff0c;如果效果要监控一台服务器物理机&#xff0c;有两种方式&#xff0c;一种是在物理机上部署“node-export”来收集数据上报给prometheus&#xff0c;另一种是“自定义监控”&#xff1b;…

SqlServer 默认值约束示例

创建表&#xff0c;创建时指定 money 字段默认值为0.00&#xff1b; create table t_24 ( account varchar(19) not null, id_card char(18) not null, name varchar(20) not null, money decimal(16,2) default 0.00 not null ); 录入2条记录&#xff0c;money字…