【STL】list的底层原理及其实现

文章目录

  • list的介绍
  • list的整体结构设计
  • list的构造
    • 代码模拟实现:
  • list节点类的实现
  • list 迭代器Iterator的使用以及实现
    • Iterator的使用
    • Iterator的底层实现
    • 反向迭代器
  • list与vector的比较
  • 实现list类

list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
  2. list的底层是用双向链表实现的(线性),每个元素都存在相互独立的节点中,每个节点都有一个指针分别指向前一个节点和后一个节点。
  3. 因为底层结构是链表,list的插入和删除操作是非常高效的,这与vector容器相反。但是由于链表的结构特点,list的各个节点之间的物理地址是不连续的,也就导致了任意访问某个节点的效率较低
  4. list的空间消耗会比vector大(存储相同个数的元素),因为每个节点还需要给前后指针开空间。

在这里插入图片描述

list的整体结构设计

list可以分为三部分:一个是list类本身,一个是节点类,一个是迭代器类。

list类的成员变量一般只有头节点(哨兵),除了负责初始化以外还负责声明和定义插入删除功能。
ListNode节点类封装了list的元素以及前后节点的指针,还负责new出节点时的初始化。
Iterator迭代器类封装了指向节点的指针ListNode*,还负责重载++、–、!=等运算符。为什么要在迭代器内部重载呢?

跟vector不同的是,由于list迭代器指向的是一个节点,且节点间物理地址不连续,向前移动或者向后移动都不能用指针直接去加减。

在这里插入图片描述
list的节点类

 // List的节点类template<class T>struct ListNode{ListNode(const T& val = T());ListNode<T>* _pPre;ListNode<T>* _pNext;T _val;};

list的迭代器类

template<class T, class Ref, class Ptr>
class ListIterator
{typedef ListNode<T>* PNode;typedef ListIterator<T, Ref, Ptr> Self;
public:ListIterator(PNode pNode = nullptr);ListIterator(const Self& l);T& operator*();T* operator->();Self& operator++();Self operator++(int);Self& operator--();Self& operator--(int);bool operator!=(const Self& l);bool operator==(const Self& l);
private:PNode _pNode;
};

list类

template<class T>
class list
{typedef ListNode<T> Node;typedef Node* PNode;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;
public:///// List的构造list();list(int n, const T& value = T());template <class Iterator>list(Iterator first, Iterator last);list(const list<T>& l);list<T>& operator=(const list<T> l);~list();///// List Iteratoriterator begin();iterator end();const_iterator begin();const_iterator end();///// List Capacitysize_t size()const;bool empty()const;};

list的构造

list有四个构造函数:无参构造、拷贝构造、连续赋值构造、迭代器构造。

//构造的list中包含n个值为val的元素
list (size_type n, const value_type& val = value_type())
//构造空的list,初始化哨兵节点
list() 
//拷贝构造
list (const list& x)
//用[first, last)区间中的元素构造list
list (InputIterator first, InputIterator last) 

代码模拟实现:

		void Empty_Init() {head = new Node;head->_pre = head;head->_next = head;head->_val = -1;sz = 0;}//构造list(){Empty_Init();}list(size_t n, const T& value = T()){Empty_Init();for (size_t i = 0; i < n; i++) {push_back(value);}sz = n;}//拷贝构造list(const list<T>& lt) {Empty_Init();for (auto& it : lt) {push_back(it);}}//迭代器构造template <class IIterator>list(IIterator first, IIterator last) {Empty_Init();while (first != last) {push_back(*first);first++;}}

list节点类的实现

节点类的成员变量就是前后指针以及节点的元素值。此外还需注意构造节点时的初始化工作。

template<class T>
class ListNode {
public:ListNode(const T& val = T()):_val(val), _pre(nullptr), _next(nullptr){}ListNode<T>* _pre;ListNode<T>* _next;T _val;
};

list 迭代器Iterator的使用以及实现

Iterator的使用

在上面iterator类的声明中我们可以看到,不同的容器其迭代器的实现方式是不一样的。在string和vector中的iterator本质是一个指针。但是list的迭代器是一个像指针的类

//返回第一个元素或最后一个的迭代器
begin()
end()//rbegin返回第一个元素的reverse_iterator,即end位置,rend返回最后一个元素下一个位置的
//reverse_iterator,即begin位置
rbegin()
rend()

在这里插入图片描述

  1. begin与end为正向迭代器,对迭代器执行++操作,迭代器向后移动
  2. rbegin(end)与rend(begin)为反向迭代器,对迭代器执行++操作,迭代器向前移动
    代码演示:
    在这里插入图片描述

Iterator的底层实现

先实现一个正向的迭代器类。因为涉及到存在const修饰迭代器指向的节点,所以在设计迭代器的时候需要同时设计出const版本(const是修饰节点,不是迭代器本身)。这里我们可以使用模板,模板里面有三个类型:节点数据类型、引用类型、指针类型
在这里插入图片描述
值得注意的是,由于我们需要在迭代器类里访问节点Node类型的成员变量,所以可以将Iterator设为Node类的友元类,或者开放Node类的权限。

迭代器的构造函数的实现:

ListIterator(Node* node):_node(node)
{}

我这里设计的比较简单,只实现了单参的构造函数,可以支持基本的隐式类型转换。

重载*:

Ref operator* () {return _node->_val;
}

重载迭代器的移动操作符++--:

Self& operator++() {_node = _node->_next;return *this;
}
Self& operator++(int) {//后置++Self temp(*this);_node = _node->_next;return temp;
}Self& operator--() {_node = _node->_pre;return *this;
}Self& operator--(int) {//后置--Self temp(_node);_node = _node->_pre;return temp;
}

重载->

Ptr operator ->() {return &_node->_val;
}

由于我们想把迭代器当指针使用,重载->是必要的,那么为什么是返回节点元素的地址呢?其实当我们在使用迭代器->时,编译器会自动优化成->->。比如我们的节点元素类型是一个类,我们有时需要访问节点元素类中的成员变量,此时希望通过迭代器->能直接访问到。
观察以下代码:
在这里插入图片描述
其中t是一个结构体类型,当我们用list存这样的节点并试图遍历节点中的a1的值时,(*it)得到的是t类型,如果我们想要输出t中的a1,就必须写成(*it).a1
我们希望迭代器能像指针一样,it->a1这样就能直接访问a1的元素,于是我们重载一个->,这个->的作用其实等价于it->->a1也就是it.operator->()->a1。这是编译器帮我们优化了。
在这里插入图片描述

反向迭代器

方向迭代器和普通的迭代器功能基本一致,只不过由于起始位置是在哨兵位,解引用时需要先往前面移动一个节点再返回其节点元素值

Ref operator* () {Self temp(_node);temp++;return temp._node->_val;
}

此外移动的方向也和普通迭代器相反,是向节点的前一个节点方向移动。

	Self& operator--() {_node = _node->_next;return *this;}Self& operator--(int) {//后置--Self temp(*this);_node = _node->_next;return temp;}Self& operator++() {_node = _node->_pre;return *this;}Self& operator++(int) {//后置++Self temp(_node);_node = _node->_pre;return temp;}

其它功能和普通迭代器几乎一致。

list与vector的比较

list和vector各种代表着的是链表和数组。它们之间的具体区别其实在前面已经讲过了。
链表的优缺点
顺序表的优缺点
迭代器的区别:

vector的迭代器是原生态指针,list的迭代器是对原生态指针(节点指针)进行封装
vector在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效。
而list插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响

实现list类

#define _CRT_SECURE_NO_WARNINGS 1
#include<assert.h>
#include<iostream>namespace bite {//节点template<class T>class ListNode {public:ListNode(const T& val = T()):_val(val), _pre(nullptr), _next(nullptr){}ListNode<T>* _pre;ListNode<T>* _next;T _val;};//反向迭代器template<class T, class Ref, class Ptr>class ReserveListIterator {public:typedef ListNode<T> Node;typedef ReserveListIterator<T, Ref, Ptr> Self;ReserveListIterator(Node* node):_node(node){}//重载Ref operator* () {Self temp(_node);temp++;return temp._node->_val;}Self& operator--() {_node = _node->_next;return *this;}Self& operator--(int) {//后置--Self temp(*this);_node = _node->_next;return temp;}Self& operator++() {_node = _node->_pre;return *this;}Self& operator++(int) {//后置++Self temp(_node);_node = _node->_pre;return temp;}bool operator!=(const Self& p) {return _node != p._node;}//T*/const T*Ptr operator ->() {return &_node->_val;}bool operator==(const Self& p) {return _node == p._node;}Node* _node;};//迭代器template<class T, class Ref, class Ptr>//T表示节点数据类型,Ref表示T&、Ptr表示T*类型class ListIterator {public:typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(Node* node):_node(node){}//重载Ref operator* () {return _node->_val;}Self& operator++() {_node = _node->_next;return *this;}Self& operator++(int) {//后置++Self temp(*this);_node = _node->_next;return temp;}Self& operator--() {_node = _node->_pre;return *this;}Self& operator--(int) {//后置--Self temp(_node);_node = _node->_pre;return temp;}bool operator!=(const Self& p) {return _node != p._node;}//T*/const T*Ptr operator ->() {return &_node->_val;}bool operator==(const Self& p) {return _node == p._node;}Node* _node;};template<class T>class list {public://节点typedef ListNode<T> Node;typedef Node* pNode;//迭代器typedef ListIterator<T, T&, T*> Iterator;typedef ListIterator<T, const T&, const T*> const_Iterator;typedef ReserveListIterator<T, T&, T*> Reserve_Iterator;typedef ReserveListIterator<T, const T&, const T*> const_Reserve_Iterator;public:void Empty_Init() {head = new Node;head->_pre = head;head->_next = head;head->_val = -1;sz = 0;}//构造list(){Empty_Init();}list(size_t n, const T& value = T()){Empty_Init();for (size_t i = 0; i < n; i++) {push_back(value);}sz = n;}//拷贝构造list(const list<T>& lt) {Empty_Init();for (auto& it : lt) {push_back(it);}}//迭代器构造template <class IIterator>list(IIterator first, IIterator last) {Empty_Init();while (first != last) {push_back(*first);first++;}}//析构~list() {Iterator it = begin();while (it != end()) {it = erase(it);}delete head;sz = 0;}void swap(list<T> lt) {std::swap(lt.head, head);std::swap(sz, lt.sz);}//普通迭代器Iterator begin() {return head->_next;}Iterator end() {return head;}//const迭代器const_Iterator begin() const {return head->_next;}const_Iterator end() const {return head;}//反向迭代器Reserve_Iterator  rbegin() {return head;}Reserve_Iterator  rend() {return head->_next;}const_Reserve_Iterator rbegin() const {return head;}const_Reserve_Iterator rend() const {return head->_next;}//插入void insert(Iterator pos, const T& val) {Node* newnode = new Node(val);Node* cur = pos._node;newnode->_pre = cur->_pre;newnode->_next = cur;cur->_pre->_next = newnode;cur->_pre = newnode;sz++;}//删除Iterator erase(Iterator pos) {assert(sz > 0);Node* cur = pos._node;Node* t = cur->_next;cur->_pre->_next = cur->_next;cur->_next->_pre = cur->_pre;delete cur;sz--;return t;}//尾插void push_back(const T& val) {insert(end(), val);//size++;}//操作符重载list<T>& operator = (list<T> lt) {swap(lt);return *this;}// List Capacitysize_t size()const {return sz;}bool empty()const {return sz == 0;}private:pNode head;size_t sz;};
}

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

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

相关文章

4.7学习总结

java学习 一.Stream流 (一.)概念: Stream将要处理的元素集合看作一种流&#xff0c;在流的过程中&#xff0c;借助Stream API对流中的元素进行操作&#xff0c;比如&#xff1a;筛选、排序、聚合等。Stream流是对集合&#xff08;Collection&#xff09;对象功能的增强&…

使用ShardingSphere报错

使用ShardingSphere报错 1604 - You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near SHOW VARIABLES LIKE sql_mode; SELECT COUNT(*) AS support_ndb FROM information_ at line 1报错…

【STL学习】(4)vector的模拟

前言 本文将模拟实现vector的常用功能&#xff0c;目的在于更深入理解vector。 一、前置知识 在模拟之前先对vector的结构和常用接口学习&#xff0c;有一个大致了解。看源码&#xff0c;本文参考的源码是SGI版本的stl3.0。 技巧&#xff1a; 看源码不要一行一行的看&#xff…

【全套源码教程】基于SpringBoot+MyBatis+Vue的流浪动物救助网站的设计与实现

目录 前言 需求分析 可行性分析 技术实现 后端框架&#xff1a;Spring Boot 持久层框架&#xff1a;MyBatis 前端框架&#xff1a;Vue.js 数据库&#xff1a;MySQL 功能介绍 前台界面功能介绍 动物领养及捐赠 宠物论坛 公告信息 商品页面 寻宠服务 个人中心 购…

全面探究 LangChain Text Splitters

全面探究 LangChain Text Splitters 0. 引言1. 文本拆分器的类型2. 探究各个文本拆分器2-1. Split by HTML header2-2. Split by HTML section2-3. Split by character2-4. Split code2-5. MarkdownHeaderTextSplitter2-6. Recursively split JSON2-7. Recursively split by ch…

力扣---分隔链表

给你一个链表的头节点 head 和一个特定值 x &#xff0c;请你对链表进行分隔&#xff0c;使得所有 小于 x 的节点都出现在 大于或等于 x 的节点之前。 你应当 保留 两个分区中每个节点的初始相对位置。 示例 1&#xff1a; 输入&#xff1a;head [1,4,3,2,5,2], x 3 输出&a…

C语言调用Python

目录 1.直接调用python语句 头文件引用 2.调用无参有参函数 1、调用无参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 2、调用有参函数 1.建立nopara.py文件 2.使用c语言根据上面流程进行调用 C语言调用python需要我们已经安装好了libpython3的 dev依赖…

DFS序列

什么是DFS序 DFS序是指对一棵树进行DFS时&#xff0c;每个节点被访问到的顺序。DFS序分成两个部分&#xff1a;进入该节点的顺序和退出该节点的顺序。 如何求DFS序 对于DFS中当前节点 1&#xff1a;计数 2&#xff1a;进入当前节点的顺序等于当前计数 3&#xff1a;想所有…

Shell脚本从0到会基础学习(个人学习)

一、前言 本人在找工作的时候&#xff0c;发现有的职位要会shell或者是python其中一种脚本语言。由于博主学过python。所以专门开栏学习&#xff0c;只做个人学习。一天学会那种~ 二、练习 2.1 我的第一个脚本--HelloWorld 代码 运行结果 2.2 输出系统常量和 自定义变量 #!…

5. 4 二重循环将二维数组的某列、某矩形转大写

5. 4 二重循环将二维数组的某列、某矩形转大写 1. 把每一行的b都变成大写 assume cs:codesg,ds:data,ss:stack data segmeNTstr db aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbcccccdb aaaaabbbbbccccc,$ data endsstack segmentdb 10 dup(0) stack endscodesg SEgments…

一种遥感影像多类变化检测方法

多任务学习孪生网络的遥感影像多类变化检测 马惠1, 刘波2, 杜世宏2 1.河南省国土空间调查规划院,郑州 450016 2.北京大学遥感与地理信息系统研究所,北京 100871 摘要: 精确掌握土地覆盖/利用的变化及变化类型对国土空间规划、生态环境监测、灾害评估等有着重要意义,然而现有…

渲染一帧特效需要多少钱?云渲染特效每帧成本

特效渲染的成本受到诸多因素的影响&#xff0c;每帧的渲染费用是评估整个项目预算的重要依据。随着云渲染技术的发展&#xff0c;其高效率和可伸缩性赢得了业界的广泛关注。对于影视制作公司和独立创作者而言&#xff0c;掌握云渲染特效的单帧成本是管理和优化预算分配的关键。…

PCL 点到三角形的距离(3D)

文章目录 一、简介二、实现代码三、实现效果参考资料一、简介 给定三角形ABC和点P,设Q为描述ABC上离P最近的点。求Q的一个方法:如果P在ABC内,那么P的正交投影点就是离P最近的点Q。如果P投影在ABC之外,最近的点则必须位于它的一条边上。在这种情况下,Q可以通过计算线段AB、…

算法:计数类dp

文章目录 一、举个栗子例子1&#xff1a;爬楼梯问题例子2&#xff1a;不同路径例子3&#xff1a;计数子序列 二、基本思路三、典型例题一、ACWing&#xff1a;900. 整数划分1、解法一1.1、状态转移方程1.2、参考代码 O(n) 超时 2、解法二&#xff1a;类似完全背包问题1.1、状态…

博客部署002-centos安装nginx

1、centos 如何安装nginx? 在CentOS系统上安装Nginx的过程相对直接&#xff0c;通常可以通过系统自带的Yum包管理器来安装。以下是安装Nginx的最新稳定版的步骤&#xff1a; 1.1 更新系统软件包 在安装Nginx之前&#xff0c;首先确保系统软件包是最新的&#xff0c;运行…

如何使用GraphQL和Apollo构建一个宝可梦应用

宝可梦是一个由视频游戏、动画系列与电影、交换卡牌游戏以及其他相关媒体组成的日本媒体特许经营权。 在本文中&#xff0c;我们将使用一个宝可梦GraphQL API&#xff0c;该API提供有关不同宝可梦的数据。 我们将使用Apollo和GraphQL来处理数据获取&#xff0c;以及React来构…

谨慎使用通过光纤传输的HDMI光纤线,存严重缺陷

严重缺陷&#xff1a; 1.只能单向传输 只能单向传输&#xff0c;从一端到另一端&#xff0c;和二极管一样&#xff0c;只能单向传输信号。某些情况你需要变更传输方向时&#xff0c;你将欲哭无泪.传统的HDMI线&#xff0c;不带放大器的&#xff0c;都可以双向传输.网上搜索布…

Vue3【进阶】

简介 https://cn.vuejs.org/guide/introduction.html 创建vue3工程 【基于 vue-cli创建】 基本和vue-cli的过程类似&#xff0c;只是选择的时候用vue3创建 【基于vite创建】【推荐】 【官网】https://vitejs.cn/ 【可以先去学一下webpack】 步骤 【https://cn.vitejs.…

#QT项目实战(天气预报)

1.IDE&#xff1a;QTCreator 2.实验&#xff1a; 3.记录&#xff1a; &#xff08;1&#xff09;调用API的Url a.调用API获取IP whois.pconline.com.cn/ipJson.jsp?iphttp://whois.pconline.com.cn/ipJson.jsp?ip if(window.IPCallBack) {IPCallBack({"ip":&quo…

华为海思2024春招数字芯片岗机试题(共9套)

huawei海思2024春招数字芯片岗机试题(共9套&#xff09;&#xff08;WX:didadidadidida313&#xff0c;加我备注&#xff1a;CSDN huawei数字题目&#xff0c;谢绝白嫖哈&#xff09; 题目包含数字集成电路、System Verilog、Verilog2001、半导体制造技术、高级ASIC芯片综合、…