当前位置: 首页 > news >正文

C++:模拟实现list

前言:

        上篇文章简单介绍了list的接口使用,那么这篇文章将通过模拟实现list以及常用接口来更好的去理解list类,观看本章前将默认读者对链表的数据结构有初步的了解。

        list在库里是一个带哨兵位的双向循环的链表结构,并且list是一个类模板并不是一个指定的类型,所以在模拟实现时需要写成模板。

list的成员变量:

  

        这里仅仅只是介绍关于list里的成员变量,对于其他出现的东西请先不要去深究,之后会有相关介绍。

        那么如上图所见,在list的成员变量里,有一个 list_node(单节点) 的类模板,它的成员变量分别是,存放的数据(val),上一个节点的指针(prev),以及下一个节点的指针(next)。

        在list类模板里只有两个成员变量,一个是头节点的指针(_head),这里是将list_node<T>这个类型typedef为Node,只是为了更好写。以及一个存放节点数量的数据(_size)。

list_node的成员函数

                

        list_node的成员函数只有一个构造函数,这个构造函数传的参数是一个T类型的数值,至于为什么不是0,是因为0是数值数据类型,而list不一定就是int类,有可能是char,double,甚至自定义类型,所以这边传的是一个T()类型(匿名对象),将T()赋值给val,再将val赋值给成员变量(_val),并将其他的两个指针都指向nullptr。

list的成员函数

        1.emptyInit()

        

       public下方的两个是typedef的迭代器(iterator)类模板暂且先不管

        emptyInit()函数:

                是负责对头节点(哨兵位)进行的初始化,即new一个节点,并将val初始化,同时将_next与_prev指针都指向自己。同时将_size初始化为0。

        2.list() 与 list( const list<T>&lt )

              

        list():

                用于默认构造,直接申请一个头节点。

        list( const list<T>&lt ):

                用于拷贝构造,申请一个头节点后,通过范围for进行单个单个节点的插入,这个要先实现迭代器所以先不着急实现。

        3.~list()

        ~list():

                析构函数,先通过clear()函数清除头节点外的所有节点(下面会实现),接着释放头节点并赋值为nullptr,再将_size赋值为0。

        4. void push_back( )

        void push_back( ):

                就是正常的双向链表的尾插,获取一个新节点,并更新尾节点,最后将头尾进行链接操作。

        5.iterator begin( )与 iterator end( )

        这里先简单理解list的iterator迭代器为一个结点指针

        iterator begin:返回的是头节点的下一个节点,及第一个有效数据的节点位置。

        iterator end:返回的是头节点。

        6.const iterator begin( ) const与 const iterator end( ) const

        const iterator begin( ) const函数:返回的是const list类型对象的头节点的下一个节点,因为const类型对象不能使用普通的迭代器,否则会造成权限放大导致程序崩溃。

         const与 const iterator end( ) const函数:返回的是const list类型对象的头节点。

        7.iterator insert(iterator pos, const T& val)

        

        iterator insert(iterator pos, const T& val)函数:就是在list的第pos个位置及第pos个节点位置前插入一个新节点。

        8.iterator erase(iterator pos)

        iterator erase(iterator pos)函数:

                删除pos位置的节点,先通过assert断言pos是否在一个正常的位置,接着重新链接pos的_prev节点与pos的_next节点。并释放pos节点,返回next节点的迭代器为了防止内部迭代器失效。

        9.size_t size()

        

        

        10.void clear()

        void clear()函数:

                创建一个list的 iterator 的 it 指针并指向list的begin位置,接着使用while循环,复用erase函数,循环删除list的有效节点,当it指针指向头节点及end位置时候就退出,保留头节点。

list的迭代器(iterator)

        首先我们要知道list的迭代器并不是普通原生的指针类型,因为list链表是各个不一样地址的空间通过指针进行链接起来的。并不像vector,string一样是一段连续的物理空间。如果仅仅只是将list迭代器++,是在单个节点的迭代器上++,并不会到达下一个节点的位置,而是变成一个野指针。

        所以,通过将迭代器进行封装为一个类模板,接着再在类模板里自己通过operator++等运算符重载来自己控制++等操作,就能解决这个这个问题。

       

 _list_iterator的成员变量:

        

        list的迭代器里面存放的不可能是一个节点,而是节点的指针,通过operator 运算符重载使得节点指针能够走向下一个或上一个节点,甚至解引用取得节点里的val。

        上图创建了一个 _list_iterator的类模板,在list的类模板了将_list_iterator类模板定义为iterator。

        接着迭代器的类里再次typedef list_node<T>为Node,_list_iterator<T,Ref,Por>为self(自己)。typed仅仅只是为了更方便以及更好读,并没有其他层次的意义。

        

        _list_iterator(Node*node )

           

                _list_iterator(Node*node )函数:

                        通过外面传入的node(节点指针)赋值给自身的_node。

        Ref operator*( )         

                

           

        Ref operator*()函数:

               通过对迭代器的理解 operator* 是将指针解引用,并返回指向的val的引用。

                所以返回类型Ref 就是 T&。

        

        self&operator++( )

        self&operator++()函数:

                根据自身理解,迭代器++会指向下一个位置,那么对于list节点++,会指向下一个节点。

                所以 _node = _node->_next; 将自身的下一个节点赋值给自身,这样_node就指向下一个节点位置,而迭代器的返回类型还是迭代器自身 self (_list_iterator<T,Ref,Por>)。

        self operator++(int)

               self operator++(int)函数:

                         self&operator++()是前置++,self operator++(int)是后置++。

                         所以需要创建一个新的迭代器对象temp并初始化temp自身的_node为*this的_node。在将this的_node指向自身的下一个位置。

        self& operator--()与self& operator--(int)

        

        Por operator->()

                   Por operator->()函数:

                        返回的是_val值的地址,因为_val不一定是内置类型,也有可能是自定义类型,而自定义类型的_val是类型的对象,需要再次解引用才能得到_val里的值。所以返回的是_val的地址,那么 Por =T*。

        

        bool operator!=(const  self& it)const与bool operator ==(const  self& it)const

        

                        operator!=比较的是两个对象,所以,这里比较的是两个迭代器类型对象的成员变量_node是否指向同一块地址空间。

        list迭代器小结:

        由于list是不连续的空间,不能使用内置的--++操作。所以我们通过封装迭代器类型,让让我们自身去控制迭代器的++与--等。由于迭代器的通用性,使得我们在上层的使用中,并不会觉得迭代器++以及--是一个复杂的操作 和普通的内置++--看起来并无差别,这就是封装带来的好处。

模拟实现list完整代码

        

#pragma once
#include<iostream>
#include<stdbool.h>
#include<assert.h>
using namespace std;namespace mabo
{template<class T>struct list_node{list_node(T val=T()):_next(nullptr),_prev(nullptr),_val(val){}T _val;list_node* _next;list_node* _prev;};template<class T, class Ref,class Por>struct _list_iterator{typedef list_node<T> Node;typedef _list_iterator<T,Ref,Por> self;_list_iterator(Node*node ):_node(node){}Ref operator*(){return _node->_val;}self&operator++(){_node = _node->_next;return *this;}self operator++(int){self temp(*this);_node = _node->_next;return temp;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self temp(*this);_node = _node->_prev;return *this;}Por operator->(){return  &_node->_val;}bool operator!=(const  self& it)const{return _node != it._node;}bool operator ==(const  self& it)const{return _node == it._node;}Node* _node;};template<class T>class list{typedef list_node<T> Node;public:typedef _list_iterator<T,T&,T*> iterator;typedef _list_iterator<T,const T&,const T*> const_iterator;void emptyInit(){_head = new Node(T());_head->_next = _head->_prev = _head;_size = 0;}list(){emptyInit();}list(const list<T>&lt){emptyInit();for (auto& e:lt){push_back(e);}}~list(){clear();delete _head;_head = nullptr;_size = 0;}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>&operator=(list<T> lt){swap(lt);return *this;}void push_back(const T&val){Node* newnode = new Node(val);Node* tail = _head->_prev;newnode->_next = _head;newnode->_prev = tail;tail->_next = newnode;_head->_prev = newnode;}void push_front(const T& val){insert(begin(), val);}iterator begin(){return _head->_next;}iterator end(){return _head;}const iterator begin() const{return _head->_next;}const iterator end() const{return _head;}iterator insert(iterator pos, const T& val){Node* newnode = new Node(val);newnode->_next = pos._node;newnode->_prev = pos._node->_prev;pos._node->_prev->_next = newnode;pos._node->_prev = newnode;_size++;return newnode; }iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_size--;return next;}size_t size(){return _size;}void clear(){iterator it = begin();while (it!=end()){it=erase(it);}_size = 0;}private:Node* _head;size_t _size;};struct A{A(int a1 = 1, int a2 = 2):_a1(a1), _a2(a2){}int _a1;int _a2;};}

        

模拟实现list测试用例

#include"2024_8_26.h"
using namespace mabo;void test01()
{mabo::list<int> li;li.push_back(1);li.push_back(2);li.push_back(3);li.push_back(4);mabo::list<int>::iterator it = li.begin();while (it!=li.end()){cout << (*it) << " ";++it;}cout << endl;for (auto e:li){cout << e << " ";}cout << endl;
}
void test02()
{list<A> li;li.push_back(A(1, 2));li.push_back(A(3, 4));mabo::list<A>::iterator it = li.begin();while (it != li.end()){cout <<it->_a1 << " "<< it->_a2<<endl;++it;}cout << endl;}void test03()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_front(5);lt.push_front(6);lt.push_front(7);lt.push_front(8);for (auto e : lt){cout << e << " ";}cout << endl;lt.clear();list<int> lt1;lt1.push_back(10);lt1.push_back(20);lt1.push_back(30);lt1.push_back(40);lt = lt1;for (auto e : lt){cout << e << " ";}cout << endl;}void test04()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;list<int> lt1 = lt;for (auto e : lt1){cout << e << " ";}cout << endl;
}void test05()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int> lt1;lt1.push_back(5);lt1.push_back(6);lt1.push_back(7);lt1.push_back(8);lt = lt1;for (auto e : lt){cout << e << " ";}cout << endl;}

相关文章:

  • 北京网站建设多少钱?
  • 辽宁网页制作哪家好_网站建设
  • 高端品牌网站建设_汉中网站制作
  • 国赛论文写作教学指南——模型的建立与求解
  • SprinBoot+Vue学生选课小程序的设计与实现
  • 全国设计院排名 工程总承包营业额二〇二三年排名
  • 线段树维护更多类型的信息
  • 数据爬虫工作中的IP清理频率
  • 代码随想录算法训练营第五十八天 | 图论part08
  • 24数学建模国赛准备!!!!(10——马氏链模型)
  • 【甲方安全建设】富文本编辑器XSS漏洞攻击及防御详析
  • Android APK打包脚本
  • JavaScript练习(二)
  • TCP数据包——报文头部组成
  • golang zap日志模块封装sentry
  • 【 html+css 绚丽Loading 】 000027 旋风破云扇
  • C++学习,指针空指针
  • 万亿低空经济:无人机飞手考证正当时
  • 【Leetcode】101. 对称二叉树
  • 【许晓笛】 EOS 智能合约案例解析(3)
  • avalon2.2的VM生成过程
  • ECS应用管理最佳实践
  • Elasticsearch 参考指南(升级前重新索引)
  • ES6语法详解(一)
  • Java基本数据类型之Number
  • java中具有继承关系的类及其对象初始化顺序
  • maven工程打包jar以及java jar命令的classpath使用
  • MySQL-事务管理(基础)
  • oldjun 检测网站的经验
  • Shell编程
  • supervisor 永不挂掉的进程 安装以及使用
  • tab.js分享及浏览器兼容性问题汇总
  • VUE es6技巧写法(持续更新中~~~)
  • 函数式编程与面向对象编程[4]:Scala的类型关联Type Alias
  • 基于OpenResty的Lua Web框架lor0.0.2预览版发布
  • 前端面试题总结
  • 深入体验bash on windows,在windows上搭建原生的linux开发环境,酷!
  • 手机app有了短信验证码还有没必要有图片验证码?
  • 我从编程教室毕业
  • 项目实战-Api的解决方案
  • 一起来学SpringBoot | 第十篇:使用Spring Cache集成Redis
  • 源码安装memcached和php memcache扩展
  • Android开发者必备:推荐一款助力开发的开源APP
  • ​LeetCode解法汇总2696. 删除子串后的字符串最小长度
  • ​用户画像从0到100的构建思路
  • # 睡眠3秒_床上这样睡觉的人,睡眠质量多半不好
  • #{} 和 ${}区别
  • #13 yum、编译安装与sed命令的使用
  • (webRTC、RecordRTC):navigator.mediaDevices undefined
  • (动态规划)5. 最长回文子串 java解决
  • (附源码)计算机毕业设计ssm-Java网名推荐系统
  • (免费领源码)python#django#mysql公交线路查询系统85021- 计算机毕业设计项目选题推荐
  • (转)Groupon前传:从10个月的失败作品修改,1个月找到成功
  • (转)Spring4.2.5+Hibernate4.3.11+Struts1.3.8集成方案一
  • (转)全文检索技术学习(三)——Lucene支持中文分词
  • (轉貼) 2008 Altera 亞洲創新大賽 台灣學生成果傲視全球 [照片花絮] (SOC) (News)
  • ./configure,make,make install的作用(转)
  • .Net - 类的介绍