当前位置: 首页 > 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…...

WPF调试神器:如何在GUI应用中优雅地输出Console日志(附完整代码)

WPF调试神器&#xff1a;如何在GUI应用中优雅地输出Console日志&#xff08;附完整代码&#xff09; 在WPF开发过程中&#xff0c;调试信息的实时输出是排查问题的关键环节。传统弹窗或文件日志方式要么打断用户体验&#xff0c;要么缺乏即时性。本文将介绍一种兼顾优雅与高效的…...

OpenClaw技能组合技:用SecGPT-14B实现ATTCK框架检测

OpenClaw技能组合技&#xff1a;用SecGPT-14B实现ATT&CK框架检测 1. 为什么需要自动化安全检测 去年处理某次安全事件时&#xff0c;我花了整整三天手工比对日志中的异常行为与ATT&CK框架。这种重复劳动让我开始思考&#xff1a;能否让AI自动完成TTPs&#xff08;战术…...

从Llama 3到GPT-4:拆解现代大模型Transformer Block的‘标配’与‘选配’(SwiGLU/Pre-Norm)

从Llama 3到GPT-4&#xff1a;现代大模型Transformer Block的架构进化论 当我们在ChatGPT中输入一个问题&#xff0c;或在Midjourney中生成一幅画作时&#xff0c;背后支撑这些AI能力的核心引擎正是Transformer架构。从2017年原始论文《Attention is All You Need》发表至今&am…...

2026网文圈变天!顶配AI写小说神器实测:除了炼字工坊,全是虚火?

搞了半个月实测&#xff0c;废了三个起点号&#xff0c;我终于把这套2026网文顶配AI组合拳盘清楚了。 说实话&#xff0c;现在市面上打着“AI写小说”旗号的工具&#xff0c;90%都是割韭菜的套壳货。 点开一看&#xff0c;全是GPT-4o或者过时的模型&#xff0c;写出来的东西一股…...

新手零困扰:在windows部署openclaw?快马ai生成手把手入门教程

新手零困扰&#xff1a;在Windows部署OpenClaw&#xff1f;快马AI生成手把手入门教程 作为一个刚接触爬虫开发的新手&#xff0c;第一次在Windows系统上部署OpenClaw时&#xff0c;我遇到了不少麻烦。从Python环境配置到各种依赖问题&#xff0c;再到运行第一个爬虫脚本&#…...

BiliTools终极指南:3分钟掌握跨平台B站资源管理工具

BiliTools终极指南&#xff1a;3分钟掌握跨平台B站资源管理工具 【免费下载链接】BiliTools A cross-platform bilibili toolbox. 跨平台哔哩哔哩工具箱&#xff0c;支持下载视频、番剧等等各类资源 项目地址: https://gitcode.com/GitHub_Trending/bilit/BiliTools 还在…...

FramePack完整指南:5个关键技巧解决AI视频生成难题

FramePack完整指南&#xff1a;5个关键技巧解决AI视频生成难题 【免费下载链接】FramePack Lets make video diffusion practical! 项目地址: https://gitcode.com/gh_mirrors/fr/FramePack 你是否曾为AI视频生成中的内存溢出、生成速度缓慢和画面漂移问题而烦恼&#x…...

Azure DevOps 自托管 Agent 如何用 Service Principal 安全接入 Azure

Azure DevOps 使用服务主体配置自托管代理配置指南1. 概述2. 在 Azure AD 中创建服务主体 (SP)3. 授予 Azure DevOps 权限3.1. 组织层级&#xff1a;用户身份与访问级别3.2. 组织层级&#xff1a;Agent pools管理员3.3. 在 Linux VM 上安装和配置代理3.4. 启动并设置为系统服务…...

新手友好:借助快马AI生成代码,零基础入门谷歌浏览器扩展开发

最近想尝试开发一个简单的谷歌浏览器扩展&#xff0c;但作为新手完全不知道从何入手。经过一番摸索&#xff0c;我发现用InsCode(快马)平台可以快速生成可运行的示例代码&#xff0c;特别适合零基础学习。下面记录下我的学习过程&#xff0c;希望能帮到同样想入门浏览器扩展开发…...

终极Goyo.vim配置指南:打造完美无干扰写作环境的10个技巧

终极Goyo.vim配置指南&#xff1a;打造完美无干扰写作环境的10个技巧 【免费下载链接】goyo.vim :tulip: Distraction-free writing in Vim 项目地址: https://gitcode.com/gh_mirrors/go/goyo.vim Goyo.vim是一款专为Vim用户设计的无干扰写作插件&#xff0c;它能帮助你…...