C++ STL专题 list的底层实现
目录
1.模拟实现list
2.节点模板讲解
3.迭代器模板讲解
3.1为什么template 有三个类型参数
(1).class T
(2).class ref
(3).class ptr
3.2 *重载
3.3 ->重载
3.4 前置++和++后置的重载
3.5 前置--和--后置的重载
3.6 ==和!=的重载
4. list模板讲解
4.1 begin()函数
4.2 end()函数
4.3 空初始化函数
4.4 深拷贝函数
4.5 交换函数
4.6 赋值运算符operator=的实现
4.7 析构函数和clear()函数
4.8 push_back函数
4.9 插入函数
4.10 push_front函数
4.11 erase函数
4.12 头删和尾删
4.13 size函数
3.14 empty函数
1.模拟实现list
分别定义了三个类模板
分别是节点模板,迭代器模板,list模板
template <class T>
class list_node
{
public:T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& data = T()): _data(data), _next(nullptr), _prev(nullptr){}
};
template <class T, class ref, class ptr>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, ref, ptr> self;Node* _node;
};
template <class T>
class list
{typedef list_node<T> Node;public:private:Node* _head;size_t _size;
};
2.节点模板讲解
源码:
template <class T>
class list_node
{
public:T _data;list_node<T>* _next;list_node<T>* _prev;list_node(const T& data = T()): _data(data), _next(nullptr), _prev(nullptr){}
};
(1).定义值域_data,用于存放数值,在定义指向下一个节点和上一个节点的指针。
(2).默认构造函数,可用于初始化,赋值等等。
3.迭代器模板讲解
源码:
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){}ref& operator*(){return _node->_data;}ptr* operator->(){return &_node->_data;}self& operator++(){_node = _node->_next;return *this;}self& operator++(int){self tmp(*this);_node = _node->_next;return tmp;}self& operator--(){_node = _node->_prev;return *this;}self& operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const self& s) const{return _node != s._node;}bool operator==(const self& s) const{return _node == s._node;}
};
3.1为什么template <class T, class ref, class ptr>有三个类型参数
(1).class T
T通常用于表示迭代器所指的数据类型。在list中,T是链表节点中存储的数据类型。例如:如果此链表用于存储整数,那么T就是int;如果链表用于存储字符串,则T就是string。
(2).class ref
ref用于定义解引用迭代器时返回的类型,这允许迭代器解引用时的行为。
在这个代码中,ref被用作operator*()的返回类型,他返回_node->_data的一个引用。这意味着ref应该是对T类型的引用类型,通常是T&。
(3).class ptr
ptr用于定义迭代器箭头操作符operator->()的返回类型。他返回_node->_data的地址,这意味着ptr应该是一个指针类型,,通常是T*。
这三个类型参数T,ref,ptr提供了对迭代器行为的精细度控制,使得迭代器模板能够更加灵活和通用地应对不同场景和数据类型。
3.2 *重载
ref& operator*()
{return _node->_data;
}
(1).*为解引用,要获取值,则返回这个值的引用即可。(ref被用作operator*()的返回类型)
3.3 ->重载
ptr* operator->()
{return &_node->_data;
}
(1). ->为获取地址,获取地址,就返回地址即可。(ref被用作operator*()的返回类型)
3.4 前置++和++后置的重载
self& operator++()
{_node = _node->_next;return *this;
}self& operator++(int)
{self tmp(*this);_node = _node->_next;return tmp;
}
(1).前置++中,由于是先自加再赋值,所以直接将_node=_node->_next即可,然后返回*this。
(1).++后置中,由于是先赋值再自加,所以要先把改变之前的值保持在tmp中,再对_node作改变,最后返回的是tmp。
3.5 前置--和--后置的重载
self& operator--()
{_node = _node->_prev;return *this;
}
self& operator--(int)
{self tmp(*this);_node = _node->_prev;return tmp;
}
(1).前置--中,由于是先自减再赋值,所以直接将_node=_node->_prev即可,然后返回*this。
(2).--后置中,由于是先赋值再自减,所以要先把改变之前的值保持在tmp中,再对_node作改变,最后返回的是tmp。
3.6 ==和!=的重载
bool operator!=(const self& s) const
{return _node != s._node;
}
bool operator==(const self& s) const
{return _node == s._node;
}
(1) ==和!=中,直接判断s与_node 是否相等即可。
(2)注意:
这两个运算符通常应该一起被重载,以保持它们之间的一致性。它们被声明为const成员函数,因为它们不修改_node成员。
4. list模板讲解
源码:
template <class T>
class list
{typedef list_node<T> Node;public:// typedef list_iterator<T> iterator;// typedef list_const_iterator<T> const_iterator;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;iterator begin(){iterator it(_head->_next);return it;}iterator end(){iterator it(_head);return it;}const_iterator begin() const{iterator it(_head->_next);return it;}const_iterator end() const{iterator it(_head);return it;}void empty_init(){_head = new Node(T());_head->_next = _head;_head->_prev = _head;_size = 0;}list(){empty_init();}list(const list<T>& lt) // 深拷贝{empty_init(); // 初始化for (auto& e : lt){push_back(e);}}list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){auto it = begin();while (it != end()){it = erase(it); // 返回下一个的迭代器}}void swap(list<int>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;_size++;// insert(end(),x);}void push_front(const T& x){insert(begin(), x);}// 在pos之前插入void insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;prev->_next = newnode;_size++;}void pop_back(){erase(--end());}void pop_front(){erase(begin());}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;_size--;return next;}size_t size() const{return _size;}bool empty(){return _size == 0;}private:Node* _head;size_t _size;
};
重命名:
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
4.1 begin()函数
iterator begin()
{iterator it(_head->_next);return it;
}
(1).begin()是一个迭代器,其作用是返回第一个有效数值的地址。
(2)._head是一个指向链表头节点的指针,这里的头节点并不存储有效数据,而是作为一个哨兵节点的存在。
(3)._head=_next是头节点的下一个节点,即链表中第一个存储有效数据的节点。
(4).iterator it(_head=_next) 创建了一个迭代器it,他初始化为指向链表的第一个有效元素。
4.2 end()函数
iterator end()
{iterator it(_head);return it;
}
(1).end()是一个迭代器,其作用是返回最后一个有效节点的下一个位置,也就是哨兵节点。
4.3 空初始化函数
void empty_init()
{_head = new Node(T());_head->_next = _head;_head->_prev = _head;_size = 0;
}
void empty_init()函数在list类中有着重要作用,其作用为初始化链表为空状态的作用,这个函数的主要目的是创建一个哑节点(哨兵节点),并设置链表的初始状态,使链表在逻辑上是空的。
4.4 深拷贝函数
list(const list<T>& lt) // 深拷贝
{empty_init(); // 初始化for (auto& e : lt){push_back(e);}
}
list(const list<T>& lt)是一个拷贝构造函数,它用于创建一个新的list对象,该对象是另一个已存在list对象的深拷贝。这个拷贝构造函数通过遍历it并使用其元素来填充新创建的list,从而实现了深拷贝。
4.5 交换函数
void swap(list<int>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}
实现了交换两个list的功能
4.6 赋值运算符operator=的实现
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}
注意是传值传参,只是一个拷贝,在调用operator=时会创建一个list<T>的副本,这个副本是通过调用list的拷贝构造函数实现的。
在swap调用后,*this获得了it的原始资源,而it获得了*this的原始资源。
4.7 析构函数和clear()函数
void clear()
{auto it = begin();while (it != end()){it = erase(it); // 返回下一个的迭代器}
}~list()
{clear();delete _head;_head = nullptr;
}
void clear()函数中,通过遍历整个list,再通过erase来销毁list。
析构函数中,直接调用了clear()函数。
4.8 push_back函数
void push_back(const T& x)
{Node* newnode = new Node(x);Node* tail = _head->_prev;//尾节点tail->_next = newnode;//更新尾节点的_next指针newnode->_prev = tail;//更新新节点的_prev指针newnode->_next = _head;//新节点的_next指向哨兵节点_head->_prev = newnode;//哨兵节点的_prev指向新节点_size++;//更新链表大小// insert(end(),x);
}
这个函数接受一个类型为constT&的参数x,即要插入的新元素的一个常量引用。使用常量引用是为了避免不必要的元素复制,同时保证传递给函数的对象不会被修改。
4.9 插入函数
void insert(iterator pos, const T& x)
{Node* cur = pos._node;//cur指向迭代器pos当前指向的节点Node* prev = cur->_prev;//prev指向cur的前一个节点Node* newnode = new Node(x);//创建一个新的节点newnode,并初始化为xnewnode->_next = cur;//将新结点的_next指针指向cur。cur->_prev = newnode;//将当前位置的_prev指针更新为指向新节点,以保持双向链接newnode->_prev = prev;//将新节点_prev指针指向prev,即前一个节点prev->_next = newnode;//将前一个结点的_next指针更新为新结点,完成插入。_size++;//链表大小加一,因为插入了新数据
}
用于在链表的指定位置pos之前插入一个新的元素x。这个函数展示了如何在双向链表中高效的插入元素。
4.10 push_front函数
void push_front(const T& x)
{insert(begin(), x);
}
直接利用insert函数即可,将插入位置定为begin()。
4.11 erase函数
iterator erase(iterator pos)
{assert(pos != end());Node* prev = pos._node->_prev;//获得删除结点的前驱Node* next = pos._node->_next;//获得删除结点的后继prev->_next = next;//将前驱结点的_next指针指向后继结点next->_prev = prev;//将后继节点的_prev指针指向前驱结点delete pos._node;//删除pos位置的节点的内存_size--;//将链表大小减一return next;//返回被删除元素之后元素的迭代器
}
用于从双向链表中删除指定位置的元素,并返回指向被删除元素之后元素的迭代器。
4.12 头删和尾删
void pop_back()
{erase(--end());
}void pop_front()
{erase(begin());
}
直接调用erase即可
4.13 size函数
size_t size() const
{return _size;
}
直接返回_size的大小即可
3.14 empty函数
bool empty(){return _size == 0;}
直接返回_size是否等于0即可。
本篇完
相关文章:

C++ STL专题 list的底层实现
目录 1.模拟实现list 2.节点模板讲解 3.迭代器模板讲解 3.1为什么template 有三个类型参数 (1).class T (2).class ref (3).class ptr 3.2 *重载 3.3 ->重载 3.4 前置和后置的重载 3.5 前置--和--后置的重载 3.6 和!的重载 4. list模板讲解 4.1 begin()函数 …...

【JavaEE】线程池
目录 前言 什么是线程池 线程池的优点 ThreadPollExecutor中的构造方法 corePoolSize && maximumPoolSize keepAliveTime && unit workQueue threadFactory 如何在java中使用线程池 1.创建线程池对象 2.调用submit添加任务 3.调用shutdown关闭线程池…...

lvs实战项目-dr模式实现
一、环境准备 主机名IP地址router eth0:172.25.254.100 eth1:192.168.0.100 clienteth0:172.25.254.200lvseth1:192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…...

JSONP跨域
1 概述 定义 json存在的意义: 不同类型的语言,都能识别json JSONP(JSON with Padding)是JSON的一种“使用模式”,可用于解决主流浏览器的跨域数据访问的问题。由于同源策略,一般来说位于 server1.example.com 的网页无法与不是 s…...

Linux--shell脚本语言—/—终章
一、shell函数 1、shell函数定义格式 参数说明: 1、可以带function fun() 定义,也可以直接fun() 定义,不带任何参数。 2、参数返回,可以显示加:return 返回,如果不加,将以最后一条命令运行结果ÿ…...

免费代理池是什么,如何使用代理IP进行网络爬虫?
互联网是一个庞大的数据集合体,网络信息资源丰富且繁杂,想要从中找到自己需要的信息要花费较多的时间。为了解决这个问题,网络爬虫技术应运而生,它的主要作用就是在海量的互联网信息中进行爬取,抓取有效信息并存储。然…...

CAN直接网络管理(20240805)
长安CAN网络管理规范 个人理解:管理CAN网络中各NM节点的工作模式(状态); 1.术语定义 👉节点地址:用于唯一标识网络中每个节点的单字节数字,取值范围是 0x00~0xFF。👉状态迁移&#x…...

HTML5+CSS3笔记(Xmind格式):第二天
Xmind鸟瞰图: 简单文字总结: 新增选择器: 1.选择相邻兄弟 2.属性选择器 3.结构性伪类选择器 4.整体结构类型 5.标签结构类型 6.指定子元素的序号 7.文本选择伪元素 8.表单中使用的状态伪类选择器 9.内容…...

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!
高清视频文件,尤其是那些以 1080p 和 720p 清晰度为特征的视频,通常都拥有相当大的体积,会占据大量计算机存储空间。因此,为了更好地将它们进行分享和存储,您可能需要对它们进行压缩,以减小它们的尺寸。然而…...

Python接口自动化测试数据提取分析:Jmespath
1、引言 在处理JSON数据时,我们常常需要提取、筛选或者变换数据。手动编写这些操作的代码不仅繁琐,而且容易出错。Python作为一个功能强大的编程语言,拥有丰富的库和工具来处理这些数据。今天,将介绍一个实用的Python库——JMESP…...
特种设备作业叉车司机题库及答案
1.在我们平时工作中,经常接触的汽油、柴油、机油、油棉纱、木材等均为() A、助燃物质 B、可燃物质 C、着火源 参考答案:B 2.叉车满载行驶时,如合成重心靠后() A、有利于纵向稳定 B、有利于横向稳定 C、纵向和横向均有利 参考答案:A 3.蓄电池车行驶中放…...

Linux 操作系统速通
一、安装虚拟机 1. VmWare 安装下载 vmware workstation pro 16 下载 win R 输入 ncpa.cpl 确保网卡正常 2. CentOS 系统下载 CentOS 系统下载 将 CentOS 系统安装到虚拟机 3. 查看虚拟机 IP 命令 ifconfig 4. finalShell 安装下载 finalShell 下载 输入用户名一般是 ro…...

IIS漏洞大全(附修复方法)
IIS6.0 IlS Server 在 Web 服务扩展中开启了 WebDAV,配置了可以写入的权限,造成任意文件上传。 漏洞复现 fofa:"llS-6.0" or 本地搭建2003 server 1)开启 WebDAV 和写权限: 做好准备工作后开启环境,然后我们去访问配置的IP&#…...

HarmonyOS笔记3:从网络数据接口API获取数据
面向HarmonyOS的移动应用一般采用MVVM模式(见参考文献【1】),其中: M(Model层):模型层,存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理…...
Mac 下生成core dump
mac下生成core dump 使用ulimit -c查看ulimit设置,显示unlimited表示开启,显示0表示关闭,通过ulimit -c unlimited打开设置; 但是这个只在当前窗口有效果。如果需要变成系统全局设置。 就需要去改/etc/profile文件,打开,然后加上ulimit -c unlimited就可…...
详解Xilinx FPGA高速串行收发器GTX/GTP(1)--SerDes和GTX的关系
目录 1、SerDes和GTX的关系 2、传输总线的变化 2.1、从串行到并行 2.2、从并行又回到串行 文章总目录点这里:《FPGA接口与协议》专栏的说明与导航 1、SerDes和GTX的关系 Hold On,这个系列文章不是讲GTX收发器的吗?怎么一开始就扯到SerDes上了?GTX和SerDes之间有…...

golang实现Digest认证鉴权接口
什么是Digest认证鉴权接口? Digest认证鉴权接口是一种基于摘要算法的身份验证方法,用于确保API请求的安全性。在实际应用中,常常使用HTTP协议的Digest认证鉴权接口来验证请求的合法性。下面是一种常见的Digest认证鉴权流程: 1. 客户端发送HTTP请求到服务器,请求接口资源…...

机房托管服务器说明
机房托管服务器是指将企业或个人的服务器放置到专业数据中心(IDC机房)进行管理和维护,由数据中心提供稳定、安全的运行环境以及网络连接等基础设施支持。rak小编为您整理发布机房托管服务器说明详细内容。 通过托管服务器到专业机房,企业能够享受到高性能…...

CookieMaker工作室合作开发C++项目十一:拟态病毒
(注:本文章使用了“无标题技术”) 一天,我和几个同事,平台出了点BUG,居然给我刷出了千年杀,同事看得瑕疵欲裂,发誓要将我挫骨扬灰—— (游戏入口:和平精英31.…...
57、PHP 实现 从扑克牌中随机抽取5张牌,判断是不是一个顺子
题目: PHP 实现 从扑克牌中随机抽取5张牌,判断是不是一个顺子 描述: 即这5张牌是不是连续的2-10位数字本身,A为1,J为11,Q为12,K为13,而大小王可以看成任意数字。 解题思路…...

如何在看板中体现优先级变化
在看板中有效体现优先级变化的关键措施包括:采用颜色或标签标识优先级、设置任务排序规则、使用独立的优先级列或泳道、结合自动化规则同步优先级变化、建立定期的优先级审查流程。其中,设置任务排序规则尤其重要,因为它让看板视觉上直观地体…...

linux arm系统烧录
1、打开瑞芯微程序 2、按住linux arm 的 recover按键 插入电源 3、当瑞芯微检测到有设备 4、松开recover按键 5、选择升级固件 6、点击固件选择本地刷机的linux arm 镜像 7、点击升级 (忘了有没有这步了 估计有) 刷机程序 和 镜像 就不提供了。要刷的时…...

分布式增量爬虫实现方案
之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面,避免重复抓取,以节省资源和时间。 在分布式环境下,增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路:将增量判…...

Selenium常用函数介绍
目录 一,元素定位 1.1 cssSeector 1.2 xpath 二,操作测试对象 三,窗口 3.1 案例 3.2 窗口切换 3.3 窗口大小 3.4 屏幕截图 3.5 关闭窗口 四,弹窗 五,等待 六,导航 七,文件上传 …...
C#学习第29天:表达式树(Expression Trees)
目录 什么是表达式树? 核心概念 1.表达式树的构建 2. 表达式树与Lambda表达式 3.解析和访问表达式树 4.动态条件查询 表达式树的优势 1.动态构建查询 2.LINQ 提供程序支持: 3.性能优化 4.元数据处理 5.代码转换和重写 适用场景 代码复杂性…...
Webpack性能优化:构建速度与体积优化策略
一、构建速度优化 1、升级Webpack和Node.js 优化效果:Webpack 4比Webpack 3构建时间降低60%-98%。原因: V8引擎优化(for of替代forEach、Map/Set替代Object)。默认使用更快的md4哈希算法。AST直接从Loa…...

PHP 8.5 即将发布:管道操作符、强力调试
前不久,PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5!作为 PHP 语言的又一次重要迭代,PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是,借助强大的本地开发环境 ServBay&am…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...

在Zenodo下载文件 用到googlecolab googledrive
方法:Figshare/Zenodo上的数据/文件下载不下来?尝试利用Google Colab :https://zhuanlan.zhihu.com/p/1898503078782674027 参考: 通过Colab&谷歌云下载Figshare数据,超级实用!!࿰…...
验证redis数据结构
一、功能验证 1.验证redis的数据结构(如字符串、列表、哈希、集合、有序集合等)是否按照预期工作。 2、常见的数据结构验证方法: ①字符串(string) 测试基本操作 set、get、incr、decr 验证字符串的长度和内容是否正…...