【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,一经查实,立即删除!

相关文章

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…

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 { //…

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…

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

随着数字化浪潮的推进&#xff0c;虚拟形象在各个领域都展现出了强大的潜力&#xff0c;美摄科技作为业界领先的数字人虚拟形象定制解决方案提供商&#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字…

HTML极速入门

HTML基础 什么是HTML HTML(Hyper Text Markup Language),超文本标记语言. 超文本:比文本更强大.通过链接和交互式方式来组织和呈现信息的文本形式.不仅仅有文本,还可能包括图片,音频,或者自己经审阅过它的学者所加的评注,补充或脚注等. 标记语言:由标签构成的语言 HTML的标…

es6 相关面试题

1 var, let ,const 区别&#xff1f; 2 手写将对象进行合并 手写合并对象 3 普通函数和箭头函数区别&#xff1f; 4 find 和 filter的区别&#xff1f; 5 some和every区别&#xff1f;

ES核心概念(45-48)(56-62)(101-103)

ES集群 ES集群&#xff08;Cluster&#xff09;包含多个节点&#xff08;服务器&#xff09;&#xff0c;整体提供服务 核心概念 索引Index&#xff1a;类似于mysql中的表 映射Mapping:数据的结构信息 文档&#xff1a;相当于表中的一条记录 分片&#xff1a; 将数据分成多片…

java 面试题总结

1锁粗化和锁消除&#xff0c;锁膨胀和锁升级的区别。 https://www.cnblogs.com/xuxinstyle/p/13387778.html .无锁 < 偏向锁 < 轻量级锁 < 重量级锁 &#xff0c;说的时候不要忘记说无锁状态 2.Map 的实现&#xff0c;线程安全的实现 1、ConcurrentHashMap在JDK 1.7…

第五套CCF信息学奥赛c++练习题 CSP-J认证初级组 中小学信奥赛入门组初赛考前模拟冲刺题(阅读程序题)

第五套中小学信息学奥赛CSP-J考前冲刺题 二、阅读程序题 (程序输入不超过数组或字符串定义的范围&#xff0c;判断题正确填√错误填X;除特殊说明外&#xff0c;判断题 1.5分&#xff0c;选择题3分&#xff0c;共计40分) 第一题 递归函数 1 #include<iostream> 2 usin…

主要用于工控主板、工业控制器、程序烧录下载器、仿真器、新能源充电桩等众多涉及RS232通讯的产品——D3232

一、应用领域 D3232芯片主要用于工控主板、工业控制器、程序烧录下载器、仿真器、新能源充电桩等众多涉及RS232通讯的产品。 二、基本特性 D3232芯片由两个线路驱动器、两个线路接收器和双电荷泵电路组成&#xff0c;具有HBM>15kV、CDM>2kV的ESD保护能力&#xff0c;并且…

NLP_文本张量表示方法_2(代码示例)

目标 了解什么是文本张量表示及其作用.文本张量表示的几种方法及其实现. 1 文本张量表示 将一段文本使用张量进行表示&#xff0c;其中一般将词汇为表示成向量&#xff0c;称作词向量&#xff0c;再由各个词向量按顺序组成矩阵形成文本表示. ["人生", "该&q…

【力扣 - 盛最多水的容器】

题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线&#xff0c;第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线&#xff0c;使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水量。 说明&#xff1a;你不能倾斜容…

【源码】imx6ull实现触摸屏单点实验-移植tslib和qt

一、本实验实验的器材&#xff1a; 1.正点原子imx6ull的阿尔法开发板v2.2 2.屏幕ALIENTEK 4.3 RGBLCD 二、实验已经移植好的文件&#xff1a; 仓库代码&#xff1a;https://gitee.com/wangyoujie11/atkboard_-linux_-driver.git 1.文件说明 arm-qt.tar.bz2&#xff1a;移植好的…

笔记本电脑里回收站删除的文件怎么找回来?这几招帮你恢复

在日常使用笔记本电脑的过程中&#xff0c;我们可能会因为不小心或者误操作&#xff0c;将一些重要的文件删除到回收站&#xff0c;甚至可能直接从回收站中清空。面对这种情况&#xff0c;很多人会感到惊慌失措&#xff0c;不知道如何是好。但其实&#xff0c;即使文件从回收站…

TQ15EG开发板教程:创建运行petalinux2019.1

工程网盘链接&#xff1a;https://pan.baidu.com/s/1vFRpzmbifXt7GypU9aKjeg 提取码&#xff1a;0ylh 首先需要使用与petalinux相同版本的vivado创建工程&#xff0c;与之前不同的是在创建硬件设计时需要勾选上添加bit文件&#xff0c;所以要在生成bit文件之后再创建硬件设计…