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,而大小王可以看成任意数字。 解题思路…...

无法与IP建立连接,未能下载VSCode服务器
如题,在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈,发现是VSCode版本自动更新惹的祸!!! 在VSCode的帮助->关于这里发现前几天VSCode自动更新了,我的版本号变成了1.100.3 才导致了远程连接出…...
Go语言多线程问题
打印零与奇偶数(leetcode 1116) 方法1:使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

消息队列系统设计与实践全解析
文章目录 🚀 消息队列系统设计与实践全解析🔍 一、消息队列选型1.1 业务场景匹配矩阵1.2 吞吐量/延迟/可靠性权衡💡 权衡决策框架 1.3 运维复杂度评估🔧 运维成本降低策略 🏗️ 二、典型架构设计2.1 分布式事务最终一致…...

五子棋测试用例
一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏,有着深厚的文化底蕴。通过将五子棋制作成网页游戏,可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家,都可以通过网页五子棋感受到东方棋类…...
【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?
FTP(File Transfer Protocol)本身是一个基于 TCP 的协议,理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况,主要原因包括: ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

若依登录用户名和密码加密
/*** 获取公钥:前端用来密码加密* return*/GetMapping("/getPublicKey")public RSAUtil.RSAKeyPair getPublicKey() {return RSAUtil.rsaKeyPair();}新建RSAUti.Java package com.ruoyi.common.utils;import org.apache.commons.codec.binary.Base64; im…...

一些实用的chrome扩展0x01
简介 浏览器扩展程序有助于自动化任务、查找隐藏的漏洞、隐藏自身痕迹。以下列出了一些必备扩展程序,无论是测试应用程序、搜寻漏洞还是收集情报,它们都能提升工作流程。 FoxyProxy 代理管理工具,此扩展简化了使用代理(如 Burp…...

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)
目录 🔍 若用递归计算每一项,会发生什么? Horners Rule(霍纳法则) 第一步:我们从最原始的泰勒公式出发 第二步:从形式上重新观察展开式 🌟 第三步:引出霍纳法则&…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上
一、软件介绍 文末提供程序和源码下载 RushDB 改变了您处理图形数据的方式 — 不需要 Schema,不需要复杂的查询,只需推送数据即可。 二、Key Features ✨ 主要特点 Instant Setup: Be productive in seconds, not days 即时设置 :在几秒钟…...