【STL】:list的模拟实现

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关list的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:C语言:从入门到精通

数据结构专栏:数据结构

个  人  主  页 :stackY、

C + + 专 栏   :C++

Linux 专 栏  :Linux

目录

1. 基本构造

2. 正向迭代器

2.1 非const迭代器

2.2 const迭代器

2.3 正向迭代器的改进优化

3. 修改相关接口

3.1 insert、erase

3.2 尾插、头插、尾删、头删、清理

4. 拷贝构造、赋值重载、析构

5. 完整代码


1. 基本构造

list的底层其实是一个带头双向循环的链表,所以在模拟实现之前可以看一下库里面怎么实现的:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _next;}//构造list(){empty_init();}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2. 正向迭代器

在使用list的阶段的迭代器使用方法和之前的string、vector一样,但是了解过链表的结构都知道,链表要访问下一个节点就是使用节点中的next指针指向下一个节点,需要访问里面的数据需要找到里面的data,++和解引用并不能访问链表的节点,那么迭代器的使用在底层肯定是封装加运算符重载。

在实现迭代器的时候也需要采用封装和运算符重载,运算符重载需要用到++、--、!=、==、*、->等运算符。迭代器分为const迭代器和非const迭代器,首先先来看看非const迭代器:

2.1 非const迭代器

迭代器的实现一般使用struct来进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器template<class T>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){return _node->_next;}//operator--self& operator--(){return _node->_prev;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*T& operator*(){return _node->_data;}//operator->T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;public://空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器iterator begin(){return iterator(_head->_next); //使用匿名对象进行构造}iterator end(){return iterator(_head);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.2 const迭代器

迭代器中涉及到修改的就是*和->,const迭代器的作用是指向的内容不能修改,本身不能修改,所以需要重新进行封装:

namespace ljm
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//...//const正向迭代器template<class T>struct __list_const_iterator{typedef list_node<T> Node;typedef __list_const_iterator<T> self;Node* _node;//迭代器构造__list_const_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*const T& operator*(){return _node->_data;}//operator->const T* operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T> iterator;typedef __list_const_iterator<T> const_iterator;public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器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);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

2.3 正向迭代器的改进优化

上面的这两种写法不难看出有很多代码都是重复出现的,使得代码非常冗余,那么怎么将这些冗余的代码进行合并呢?

我们可以看一下库里面如何实现:

采用泛型编程,使用模板,将这一转化的工作交给编译器,而我们只需传递const参数和非const参数即可:

namespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}///正向迭代器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);}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

3. 修改相关接口

3.1 insert、erase

在pos位置进行插入删除比较简单,需要进行链接新节点,改变节点中指针的指向即可,库里面对于插入和删除都带有返回值,那么我们在实现的时候也加上返回值:

///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}

在进行insert之后可以看到迭代器是不会失效的,但是在erase之后pos位置会被释放,因此erase之后迭代器会失效,在使用前先更新迭代器。

3.2 尾插、头插、尾删、头删、清理

当实现了insert和erase之后,实现这些接口直接复用即可:

        //尾插void push_back(const T& x){insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}

4. 拷贝构造、赋值重载、析构

在这里我们都是采用现代写法:

 拷贝构造直接使用迭代器依次遍历进行尾插。

//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=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;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}

5. 完整代码

 头文件:List.h

#pragma oncenamespace ywh
{//链表结构template<class T>struct list_node{T _data;                 //节点中的数据list_node<T>* _prev;    //指向前一个节点的指针list_node<T>* _next;    //指向后一个节点的指针//构造list_node(const T& x = T()):_data(x), _prev(nullptr), _next(nullptr){}};//非const正向迭代器//   类型模板参数   传递引用      传递指针template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//迭代器构造__list_iterator(Node* node):_node(node){}//前置//operator++self& operator++(){_node = _node->_next;return *this;}//operator--self& operator--(){_node = _node->_prev;return *this;}//后置self operator++(int){self* tmp(_node);_node = _node->_next;return tmp;}//operator--self operator--(int){self* tmp(_node);_node = _node->_prev;return tmp;}//operator*Ref operator*(){return _node->_data;}//operator->Ptr operator->(){return &_node->_data;}//operator!=bool operator!=(const self& s){return _node != s._node;}//operator==bool operator==(const self& s){return _node == s._node;}};//list结构template<class T>class list{public:typedef list_node<T> Node;typedef __list_iterator<T, T&, T*> iterator;   //非const迭代器typedef __list_iterator<T, const T&, const T*> const_iterator;  //const迭代器public:基本构造/////空初始化void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;}//构造list(){empty_init();}//拷贝构造list(const list<T>& lt){//先创建空节点empty_init();//依次尾插即可for (auto e : lt){push_back(e);}}//operator=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;}//析构~list(){clear();delete _head;_head = nullptr;_size = 0;}///正向迭代器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);}///修改相关接口//在pos位置插入节点iterator insert(iterator pos, const T& x){//创建新新节点Node* newnode = new Node(x);//链接节点Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;//更新节点个数++_size;//返回新节点的位置return iterator(newnode);}//删掉pos位置的节点iterator erase(iterator pos){//保存相对位置Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//链接节点delete cur;prev->_next = next;next->_prev = prev;//更新节点个数--_size;//返回pos的下一个位置return iterator(next);}//尾插void push_back(const T& x){insert(end(), x);}//头插void push_front(const T& x){insert(begin(), x);}//尾删void pop_back(){erase(--end());}//头删void pop_front(){erase(begin());}//清理void clear(){iterator it = begin();while (it != end()){it = erase(it);}}//节点个数size_t size(){return _size;}private:Node* _head;  //链表的头节点size_t _size; //节点个数};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!  

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

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

相关文章

前端如何不变形的渲染图片大小和图片上的内容

在做前端项目时可能经常会页面图片大小变形或者压缩的情况&#xff0c;一般情况就是height给100%&#xff0c;width给auto就可以了满足大部分使用情况了。有时候需要做一些比较复杂的功能&#xff0c;比如需要在图片上增加锚点&#xff0c;而且图片在适配各种屏幕大小时&#x…

【Midjourney入门教程1】Midjourney的注册、订阅

文章目录 前言一、Midjourney是什么二、Midjourney注册三、新建自己的服务器四、开通订阅 前言 AI绘画即指人工智能绘画&#xff0c;是一种计算机生成绘画的方式。是AIGC应用领域内的一大分支。 AI绘画主要分为两个部分&#xff0c;一个是对图像的分析与判断&#xff0c;即“…

Linux编译器vim的使用

文章目录 vim基本概念vim的常用三种模式vim三种模式的相互转换 vim命令模式下的命令集移动光标删除文字剪切/删除复制替换撤销和恢复跳转至指定行 vim底行模式下的命令集 vim基本概念 vim是Linux下的一个多模式的编译器 简单来说就是写代码的工具 不提供编译调试等功能 有语法…

Unity中Shader的GI相关数据的准备

文章目录 前言一、把 Unity 中用到的 GI 的函数移植到我们自定义的 cginc 文件中二、开始使用和 GI 相关的方法1、了解 UnityGI 结构体的内容,并且准备 UnityGI 的数据2、了解 SurfaceOutput 结构体&#xff0c;并且准备数据3、了解并准备 UnityGIInput 结构体&#xff0c;并且…

修复国产电脑麒麟系统开机出现initramfs 问题

目录预览 一、问题描述二、原因分析三、解决方案四、知识点呀initramfsBusyBox 五、参考链接 一、问题描述 国产麒麟系统出现 initramfs 模式 二、原因分析 一般在拷贝卡顿过程【强制关机】或者电【脑异常断电】的情况下概率性导致系统分区损坏&#xff0c;重启后大概率就会进…

Redis的介绍,以及Redis的安装(本机windows版,虚拟机Linux版)和Redis常用命令的介绍

目录 一. Redis简介 二. Redis的安装 2.1 Linux版安装 2.2 windows版安装 三. Redis的常用命令 一. Redis简介 Redis是一个开源&#xff08;BSD许可&#xff09;&#xff0c;内存存储的数据结构服务器&#xff0c;可用作数据库&#xff0c;高速缓存和消息队列代理。 它…

玩一下Spring Boot

文章目录 1 开发环境1.1 JDK1.2 IntelliJ IDEA2 Spring Boot2.1 创建项目2.2 创建模板页面2.3 创建控制器2.4 启动项目2.5 访问页面1 开发环境 1.1 JDK 安装JDK21 配置环境变量 在命令行查看JDK版本 玩一玩jshell

服务器基本命令

nohup python3 ./download-all-2023-11-01_12-08-11.py > T85_download.log & 标准输出重定向到文件 nohup bash test.sh > stdout.txt & 标准错误输出重定向到文件 nohup bash test.sh 2> stderr.txt & 重定向到不同文件 nohup bash test.sh > …

Find My卡片|苹果Find My技术与卡片结合,智能防丢,全球定位

钱包是许多人正常生活必备的物品&#xff0c;如果丢失钱包&#xff0c;不仅是钱的损失&#xff0c;还因为丢失了证件和银行卡&#xff0c;导致我们需要话费大量时间去补办&#xff0c;造成生活的不便。如今防丢卡片的出现将减少这类问题的发生。 在智能化加持下&#xff0c;防丢…

2014年亚太杯APMCM数学建模大赛A题无人机创造安全环境求解全过程文档及程序

2014年亚太杯APMCM数学建模大赛 A题 无人机创造安全环境 原题再现 20 国集团&#xff0c;又称 G20&#xff0c;是一个国际经济合作论坛。2016 年第 11 届 20 国集团峰会将在中国召开&#xff0c;这是继 APEC 后中国将举办的另一个大型峰会。此类大型峰会&#xff0c;举办城市…

【Azure】存储服务:Azure 的存储账户

文章目录 一、前提知识&#xff08;建议了解&#xff09;二、介绍 Azure 存储帐户三、使用 Microsoft Azure 门户创建存储帐户 一、前提知识&#xff08;建议了解&#xff09; 在每一个云厂商中&#xff0c;都有自身的云存储&#xff0c;也有根据不同功能进行区分的不同类型的…

【好玩的开源项目】Linux系统之部署捕鱼达人经典小游戏

【好玩的开源项目】Linux系统之部署捕鱼达人经典小游戏 一、捕鱼达人小游戏介绍1.1 捕鱼达人小游戏简介1.2 项目地址 二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍 三、安装httpd软件3.1 检查yum仓库3.2 安装httpd软件3.3 启动httpd服务3.4 查看httpd服务3.5 防火墙和sel…