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

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&#xff1a;172.25.254.100 eth1&#xff1a;192.168.0.100 clienteth0&#xff1a;172.25.254.200lvseth1&#xff1a;192.168.0.50web1web2 1、client配置 [rootclient ~]# cat /etc/NetworkManager/system-connections/eth0.nmconne…...

JSONP跨域

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

Linux--shell脚本语言—/—终章

一、shell函数 1、shell函数定义格式 参数说明&#xff1a; 1、可以带function fun() 定义&#xff0c;也可以直接fun() 定义,不带任何参数。 2、参数返回&#xff0c;可以显示加&#xff1a;return 返回&#xff0c;如果不加&#xff0c;将以最后一条命令运行结果&#xff…...

免费代理池是什么,如何使用代理IP进行网络爬虫?

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

CAN直接网络管理(20240805)

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

HTML5+CSS3笔记(Xmind格式):第二天

Xmind鸟瞰图&#xff1a; 简单文字总结&#xff1a; 新增选择器&#xff1a; 1.选择相邻兄弟 2.属性选择器 3.结构性伪类选择器 4.整体结构类型 5.标签结构类型 6.指定子元素的序号 7.文本选择伪元素 8.表单中使用的状态伪类选择器 9.内容…...

视频压缩文件太大了怎么缩小?6个视频压缩技巧,速度收藏起来!

高清视频文件&#xff0c;尤其是那些以 1080p 和 720p 清晰度为特征的视频&#xff0c;通常都拥有相当大的体积&#xff0c;会占据大量计算机存储空间。因此&#xff0c;为了更好地将它们进行分享和存储&#xff0c;您可能需要对它们进行压缩&#xff0c;以减小它们的尺寸。然而…...

Python接口自动化测试数据提取分析:Jmespath

1、引言 在处理JSON数据时&#xff0c;我们常常需要提取、筛选或者变换数据。手动编写这些操作的代码不仅繁琐&#xff0c;而且容易出错。Python作为一个功能强大的编程语言&#xff0c;拥有丰富的库和工具来处理这些数据。今天&#xff0c;将介绍一个实用的Python库——JMESP…...

特种设备作业叉车司机题库及答案

1.在我们平时工作中&#xff0c;经常接触的汽油、柴油、机油、油棉纱、木材等均为() A、助燃物质 B、可燃物质 C、着火源 参考答案:B 2.叉车满载行驶时&#xff0c;如合成重心靠后() 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&#xff0c;配置了可以写入的权限&#xff0c;造成任意文件上传。 漏洞复现 fofa:"llS-6.0" or 本地搭建2003 server 1)开启 WebDAV 和写权限: 做好准备工作后开启环境&#xff0c;然后我们去访问配置的IP&#…...

HarmonyOS笔记3:从网络数据接口API获取数据

面向HarmonyOS的移动应用一般采用MVVM模式&#xff08;见参考文献【1】&#xff09;&#xff0c;其中&#xff1a; M&#xff08;Model层)&#xff1a;模型层&#xff0c;存储数据和相关逻辑的模型。它表示组件或其他相关业务逻辑之间传输的数据。Model是对原始数据的进一步处理…...

Mac 下生成core dump

mac下生成core dump 使用ulimit -c查看ulimit设置,显示unlimited表示开启,显示0表示关闭,通过ulimit -c unlimited打开设置; 但是这个只在当前窗口有效果。如果需要变成系统全局设置。 就需要去改/etc/profile文件&#xff0c;打开&#xff0c;然后加上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机房)进行管理和维护&#xff0c;由数据中心提供稳定、安全的运行环境以及网络连接等基础设施支持。rak小编为您整理发布机房托管服务器说明详细内容。 通过托管服务器到专业机房&#xff0c;企业能够享受到高性能…...

CookieMaker工作室合作开发C++项目十一:拟态病毒

&#xff08;注&#xff1a;本文章使用了“无标题技术”&#xff09; 一天&#xff0c;我和几个同事&#xff0c;平台出了点BUG&#xff0c;居然给我刷出了千年杀&#xff0c;同事看得瑕疵欲裂&#xff0c;发誓要将我挫骨扬灰—— &#xff08;游戏入口&#xff1a;和平精英31.…...

57、PHP 实现 从扑克牌中随机抽取5张牌,判断是不是一个顺子

题目&#xff1a; PHP 实现 从扑克牌中随机抽取5张牌&#xff0c;判断是不是一个顺子 描述&#xff1a; 即这5张牌是不是连续的2-10位数字本身&#xff0c;A为1&#xff0c;J为11&#xff0c;Q为12&#xff0c;K为13&#xff0c;而大小王可以看成任意数字。 解题思路&#xf…...

无法与IP建立连接,未能下载VSCode服务器

如题&#xff0c;在远程连接服务器的时候突然遇到了这个提示。 查阅了一圈&#xff0c;发现是VSCode版本自动更新惹的祸&#xff01;&#xff01;&#xff01; 在VSCode的帮助->关于这里发现前几天VSCode自动更新了&#xff0c;我的版本号变成了1.100.3 才导致了远程连接出…...

Go语言多线程问题

打印零与奇偶数&#xff08;leetcode 1116&#xff09; 方法1&#xff1a;使用互斥锁和条件变量 package mainimport ("fmt""sync" )type ZeroEvenOdd struct {n intzeroMutex sync.MutexevenMutex sync.MutexoddMutex sync.Mutexcurrent int…...

Python 训练营打卡 Day 47

注意力热力图可视化 在day 46代码的基础上&#xff0c;对比不同卷积层热力图可视化的结果 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…...

消息队列系统设计与实践全解析

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

五子棋测试用例

一.项目背景 1.1 项目简介 传统棋类文化的推广 五子棋是一种古老的棋类游戏&#xff0c;有着深厚的文化底蕴。通过将五子棋制作成网页游戏&#xff0c;可以让更多的人了解和接触到这一传统棋类文化。无论是国内还是国外的玩家&#xff0c;都可以通过网页五子棋感受到东方棋类…...

【FTP】ftp文件传输会丢包吗?批量几百个文件传输,有一些文件没有传输完整,如何解决?

FTP&#xff08;File Transfer Protocol&#xff09;本身是一个基于 TCP 的协议&#xff0c;理论上不会丢包。但 FTP 文件传输过程中仍可能出现文件不完整、丢失或损坏的情况&#xff0c;主要原因包括&#xff1a; ✅ 一、FTP传输可能“丢包”或文件不完整的原因 原因描述网络…...

若依登录用户名和密码加密

/*** 获取公钥&#xff1a;前端用来密码加密* 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

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

数据结构:泰勒展开式:霍纳法则(Horner‘s Rule)

目录 &#x1f50d; 若用递归计算每一项&#xff0c;会发生什么&#xff1f; Horners Rule&#xff08;霍纳法则&#xff09; 第一步&#xff1a;我们从最原始的泰勒公式出发 第二步&#xff1a;从形式上重新观察展开式 &#x1f31f; 第三步&#xff1a;引出霍纳法则&…...

RushDB开源程序 是现代应用程序和 AI 的即时数据库。建立在 Neo4j 之上

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