STL:List从0到1

请添加图片描述

在这里插入图片描述

🎉个人名片

🐼作者简介:一名乐于分享在学习道路上收获的大二在校生
🙈个人主页🎉:GOTXX
🐼个人WeChat:ILXOXVJE
🐼本文由GOTXX原创,首发CSDN🎉🎉🎉
🐵系列专栏:零基础学习C语言----- 数据结构的学习之路----C++的学习之路
🐓每日一句:如果没有特别幸运,那就请特别努力!🎉🎉🎉
————————————————

🎉文章简介

🎉本篇文章将 介绍如何使用C++编写代码来实现一个类似于STL中的List容器 相关知识进行分享!
💕如果您觉得文章不错,期待你的一键三连哦,你的鼓励是我创作动力的源泉,让我们一起加 油,一起奔跑,让我们顶峰相见!!!🎉🎉🎉
——————————————————

一.前言

这篇文章将介绍如何使用C++编写代码来实现一个类似于STL中的List容器。 list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器。在这篇文章中,你将学习并实现List的常见功能,如添加元素、删除元素等。通过实现自己的List容器,你将更好地熟悉List的使用及相关特性,并提升对C++语言的理解和掌握。
————————————————

二.List的介绍

List文档介绍链接: link

1. list是一个可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代;
2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素;
3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效;
4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好(不需要挪动数据);
5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素);

三.List的结构及模拟实现

一.底层结构

List底层是一个带头双向循环链表

如图:
在这里插入图片描述
在这里插入图片描述
库里面的begin() 与end() 返回的节点位置:

begin()返回的是头节点的下一个节点;
而end()返回的是头节点;

二.List的模拟实现

重点::迭代器的实现

1. 构造函数
template<class T>
struct ListNode
{ListNode<T>(const T& x=T())    :_next(nullptr),_prev(nullptr),_val(x){}ListNode<T>* _next;ListNode<T>* _prev;T _val;
};

库里面的List类的构造函数是另写一个函数,因为这个函数拷贝构造会使用,然后复用它,所以我们也这样实现;

template<class T>
class List
{
public:void empty_List(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}List(){empty_List();}
}
2. 拷贝构造函数
List(const List<T>& lt)
{empty_List();     //初始化for (const auto& e : lt)    {push_back(e);        //将lt里面的数据依次尾插}
}
3. 插入函数

思路:记录前一个和后一个节点,然后连接

在这里插入图片描述

iterator insert(iterator pos, const T& x)
{node* newnode = new node(x);    //构造一个节点node* next = pos._node;         node* prev = next->_prev;      //记录前一个newnode->_next = next;         next->_prev = newnode;         //链接newnode->_prev = prev;prev->_next = newnode;return pos;
}
4. 尾插函数

复用insert函数

void push_back(const T& x)
{
/*	node* newnode = new node(x);node* tail = _phead->_prev;tail->_next = newnode;newnode->_prev = tail;       //不复用的写法newnode->_next = _phead;_phead->_prev = newnode;*/insert(end(), x);
}
5. 头插函数

复用insert函数

	void push_front(const T& x){insert(begin(),x);}
6. 删除函数
iterator erase(iterator pos)
{assert(pos!=end());node* prev = pos._node->_prev;     //保存前一个节点node* next = pos._node->_next;     //保存后一个节点prev->_next = next;               //连接next->_prev = prev;delete pos._node;                  //释放掉该节点return next;                       //返回删除元素的下一个节点
}
7. 尾删函数

复用删除函数

	void pop_back(){//erase(end()._node->_prev);erase(--end());       //头节点的前指针指向的是最后一个节点}
8. 头删函数

复用删除函数

	void pop_front(){erase(begin());     }
9. 迭代器的实现

因为链表的底层物理空间不是连续的,所以不能使用原生指针类实现。因为原生指针++,可以找到下一个数据,但是链表的节点与节点之间不是连续的,指针++,不能找到下一个节点,所以我们需要操作符重载,改变 ++, != ,* 等操作符的行为;又因为节点指针是内置类型,不能进行操作符重载,所以我们只能将它进行封装,封装在一个类里面,进行重载;

template<class T>
struct __List_iterator       
{typedef ListNode<T> node;typedef __List_iterator<T> self;__List_iterator(node* node)       //构造函数:_node(node){}self& operator++()               //运算符的重载{_node = _node->_next;       //前置++,返回++后的值return *this;}self& operator++(int){self tmp(_node);         //保存++前的值_node = _node->_next;return tmp;         //返回++前的值}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(_node);_node = _node->_prev;return tmp;}T& operator*(){return _node->_val;}bool operator!=(const self& s){return s._node!= this->_node;}bool operator==(const self& s){return s._node == this->_node;}node* _node;
};
10. 赋值运算符重载

传统写法:

	void clear(){iterator lt = begin();while (lt != end()){lt = erase(lt);}}
//lt1=lt2
List<T>& operator=(const List<T>& lt)
{clear();                                 //清空函数,将链表中的有效数据删除掉,保留头节点for (const auto& e : lt){push_back(e);            //依次尾插}return *this;
}

现代写法:

void swap(List<T>& lt)
{std::swap(_phead, lt->_phead);
}
//lt1=lt2
List<T>& operator=(List<T> lt)    //lt是lt2的拷贝构造
{swap(lt);      //交换lt与lt1return *this;    //返回
}
补充知识:

typedef 放在类里面与外面的区别:
如果是放在公有里面,则类外面也可以使用,但是要指定类域;
如果是私有的话,则类外面不能使用;

三.总代码:

#pragma once
#include<iostream>
#include<assert.h>
using namespace std;namespace L
{template<class T>struct ListNode{ListNode<T>(const T& x=T()):_next(nullptr),_prev(nullptr),_val(x){}ListNode<T>* _next;ListNode<T>* _prev;T _val;};template<class T>struct __List_iterator{typedef ListNode<T> node;typedef __List_iterator<T> self;__List_iterator(node* node):_node(node){}self& operator++(){_node = _node->_next;return *this;}self& operator++(int){self tmp(_node);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(_node);_node = _node->_prev;return tmp;}T& operator*(){return _node->_val;}bool operator!=(const self& s){return s._node!= this->_node;}bool operator==(const self& s){return s._node == this->_node;}node* _node;};template<class T>class List{public:typedef ListNode<T> node;typedef __List_iterator<T>  iterator;iterator begin(){return iterator(_phead->_next);}iterator end(){return iterator(_phead);}void empty_List(){_phead = new node;_phead->_next = _phead;_phead->_prev = _phead;}List(){empty_List();}List(const List<T>& lt){empty_List();for (const auto& e : lt)    //引用更好,如果T类型是自定义类型的话{push_back(e);}}//lt1=lt2List<T>& operator=(const List<T>& lt){clear();for (const auto& e : lt){push_back(e);}return *this;}void swap(List<T>& lt){std::swap(_phead, lt->_phead);}//lt1=lt2List<T>& operator=(List<T> lt){swap(lt);return *this;}void clear(){iterator lt = begin();while (lt != end()){lt = erase(lt);}}~List(){clear();delete _phead;_phead = nullptr;}void push_back(const T& x){/*	node* newnode = new node(x);node* tail = _phead->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _phead;_phead->_prev = newnode;*/insert(end(), x);}void push_front(const T& x){insert(begin(),x);}iterator insert(iterator pos, const T& x){node* newnode = new node(x);node* next = pos._node;node* prev = next->_prev;newnode->_next = next;next->_prev = newnode;newnode->_prev = prev;prev->_next = newnode;return pos;}iterator erase(iterator pos){assert(pos!=end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return next;}void pop_back(){//erase(end()._node->_prev);erase(--end());}void pop_front(){erase(begin());}private:node* _phead;};

请添加图片描述

0

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

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

相关文章

从政府工作报告探究计算机行业发展

从政府工作报告探计算机行业发展 政府工作报告作为政府工作的全面总结和未来规划&#xff0c;不仅反映了国家整体的发展态势&#xff0c;也为各行各业提供了发展的指引和参考。随着信息技术的快速发展&#xff0c;计算机行业已经成为推动经济社会发展的重要引擎之一。因此&…

【蓝桥杯单片机】十三届省赛“重难点”解析(附源码)

【蓝桥杯单片机】十三届省赛“重难点”解析 一、题目难点解析二、易出错点提示三、完整代码链接 笔记包括&#xff1a;①题目难点解析、②易出错点提示、③完整代码链接 注&#xff1a;本文提供的所有代码都是使用第十四届竞赛包完成 ⭐----------系列文章链接----------⭐ 【蓝…

面试经典-MySQL篇

一、MySQL组成 MySQL数据库的连接池&#xff1a;由一个线程来监听一个连接上请求以及读取请求数据&#xff0c;解析出来一条我们发送过去的SQL语句SQL接口&#xff1a;负责处理接收到的SQL语句查询解析器&#xff1a;让MySQL能看懂SQL语句查询优化器&#xff1a;选择最优的查询…

网络安全——关于防火墙

网络安全防火墙是很重要的部分&#xff0c;关于防火墙我们要知道&#xff0c;他默认所有流量都是黑名单&#xff0c;只有开启允许通过才可以。 我们通过一个实验来学防火墙命令。 防火墙要登录才能使用&#xff0c;用户名是admin,默认密码是Admin123&#xff0c;在第一次登录…

初识进程状态

&#x1f30e;进程状态【上】 文章目录&#xff1a; 进程状态 发现进程的状态 运行队列 进程排队 进程状态的表述       状态在代码中的表示       运行状态       阻塞状态       挂起状态 总结 前言&#xff1a; 为了搞明白正在运行的进程是什么意思…

MC78L05ACDR2G线性稳压器芯片中文资料规格书PDF数据手册引脚图参数图片价格

产品概述&#xff1a; MC78L00A系列线性稳压器价格便宜&#xff0c;易于使用&#xff0c;适用于各种需要最高100mA的调节电源的应用。与大功率MC7800和MC78M00系列一样&#xff0c;这款稳压器也提供内部电流限制和高温关断&#xff0c;因此非常坚固耐用。在很多应用中&#xf…

R语言:如何基于地球外辐射(Ra)和相对日照(n/N)计算太阳辐射Rs?

正在编写相关软著&#xff0c;借此机会了解R语言的基本语法和一些处理流程&#xff0c;所以解释稍微繁琐。 Note&#xff1a; 使用的R语言版本是 R version 4.3.2 (2023-10-31 ucrt) 使用的RStudio编辑器版本是&#xff1a; 01 基于随机森林的插值填补缺失值 这是目前处理…

冲动是魔鬼,工作不顺心时不要把坏脾气带给家人

今天与一个跟踪了很久的客户准备签合同了&#xff0c;客户突然反悔&#xff0c;为此与他周旋了一整天&#xff0c;忙碌得一口水都没有喝。回到小区坐在车里抽着烟&#xff0c;久久不愿回家&#xff0c;只想一个人坐着&#xff0c;疲惫、无奈。这个月的奖金似乎又将成为泡影。 …

MySQL语法分类 DDL

DDL(操作数据库、表) 数据库操作(CRUD) C(Create):创建 //指定字符集创建 create database db_1 character set utf8;//避免重复创建数据库报错可以用一下命令 create database if not exists db_1 character set utf8;R(Retrieve):查询 //查询所有数据库的名称 show datab…

spark基本原理UI界面解读

这里是引用 1 八股文 1.1 基本原理 driver节点是整个应用程序的指挥所 指挥官是sparkcontext 环境&#xff1a;构建一个集群 应用程序提交 确定主节点&#xff0c;确定指挥所driver&#xff0c;确定指挥官sparkcontext sparkcontext会向资源管理器申请资源 会将作业分…

拿捏指针(二)

个人主页&#xff1a;秋邱博客 所属栏目&#xff1a;C语言 &#xff08;感谢您的光临&#xff0c;您的光临蓬荜生辉&#xff09; 目录 前言 数组与指针 数组名的理解 指针数组与数组指针 指针数组 数组指针 数组传参 一维数组传参的本质 二维数组传参的本质 二维数组…

【应急响应靶场web2】

文章目录 前言 一、应急响应 1、背景 2、webshell查杀 3、日志排查 1&#xff09;apache日志 2&#xff09;nginx日志 3&#xff09;ftp日志 4、隐藏账户 5、文件筛选 二、漏洞复现 总结 前言 靶场来源&#xff1a;知攻善防实验室 一、应急响应 1、背景 小李在某…