解密list的底层奥秘

在这里插入图片描述

🎈个人主页:🎈 :✨✨✨初阶牛✨✨✨
🐻强烈推荐优质专栏: 🍔🍟🌯C++的世界(持续更新中)
🐻推荐专栏1: 🍔🍟🌯C语言初阶
🐻推荐专栏2: 🍔🍟🌯C语言进阶
🔑个人信条: 🌵知行合一
金句分享:
✨即使人生还有无数次失望的可能性,✨
✨但我还是想活在理想主义的乌托邦里.✨

目录

  • 一、list底层框架
    • (1) 节点类
    • (2) 迭代器类
    • (3) list类
  • 二、构造函数
    • (1) 无参构造
    • (2) n个value构造
    • (3) 迭代器区间构造
    • (4) 拷贝构造
  • 三、迭代器
    • 迭代器的实现
      • ① 构造
      • ② `*`和`->`
      • ③ 前置++与后置++
      • ④ 前置--与后置--
      • ⑤ 比较运算符
    • list类中的迭代器
    • front和back
  • 四、Modifiers:
    • (1) insert()
    • (2) erase()
    • (3) push_back() 和 push_front()
    • (4) pop_back() 和 pop_front()
    • (5) clear()
    • (6) swap()
  • 结语

本篇通过模拟实现list的构造函数,迭代器,和部分成员函数以帮助大家更加深层的理解list的原理,希望看完这篇文章使得友友们对list有了更加深层的理解.

一、list底层框架

list的底层是一个带头双向循环链表.
在这里插入图片描述

(1) 节点类

因为list中节点可能存储各种类型的值,所以这里使用了一个模板参数T.

list <int>
list<doubel>

 	// List的节点类template<class T>struct list_node{list_node(const T& val = T()):_val(val){}//这里的 T() 表示使用类型 T 的默认构造函数创建一个对象,//它将调用 T 类型的默认构造函数来初始化 val。如果类型 T 没有提供默认构造函数,那么代码将无法编译通过。list_node<T>* _prev=nullptr;	//指向前驱结点list_node<T>* _next=nullptr;	//指向后继结点T _val;							//存储数据};

(2) 迭代器类

很多小伙伴会疑问,为什么一个迭代器类却使用了三个模板参数,是不是有些多余呢?
在这里插入图片描述

其实不然,牛牛依次为大家解释.

  1. class T: 是结点类的存储不同数据所需要使用的模板参数.该模板参数表示要处理的元素的类型。它可以是任意类型,例如整数、浮点数、自定义类等等。在模板实例化时,需要提供一个具体的类型。

  2. Ref: 该模板参数表示指向元素类型 T引用。它定义了对元素的引用类型,在实例化模板时,将使用指定的引用类型来操作元素。

  3. Ptr: 该模板参数表示指向元素类型 T指针。它定义了指向元素的指针类型,在实例化模板时,将使用指定的指针类型来操作元素。

template<class T, class Ref, class Ptr>
意味着
list_iterator<T, const T&, const T*>;

list的迭代器用来遍历链表中的元素,外部通过迭代器的++--进行链表的元素访问,这是一种封装,隐藏内部list的实现细节,外部只能通过迭代器的方式访问.

	//迭代器类template<class T, class Ref, class Ptr>struct list_iterator{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> self;//self表示自己list_iterator(Node* node);	//构造list_iterator(const self& list);     //拷贝构造Ref operator*();Ptr operator->();//前置++self& operator++();  //后置++self operator++(int);//前置--self& operator--();//后置--self& operator--(int);bool operator!=(const self& list) const; bool operator==(const self& list);//成员变量Node* _node;};

(3) list类

  template<class T>class list{typedef list_node<T> Node;public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;list()//(无参构造)//n个value构造list(int n, const T& value = T());//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last);    //拷贝构造list(const list<T>& list);//各种成员函数/...//析构函数~list();iterator begin();       iterator end();//常属性迭代器const_iterator begin()const;const_iterator end()const; private:Node* _head;size_t _size;};

二、构造函数

对于带头双向循环链表,它的初始化操作是必须的,因为必须创建一个头指针.
在这里插入图片描述
对于list的构造函数,它是很多种方式的,例如:无参构造,nval构造,迭代器区间构造等.
对于每个构造,必须前进行最初的初始化操作,为了避免代码冗余,我们将这个部分单独写成一个初始化操作的函数.

如下:

 void  Init_List(){_head = new Node;	//创建头指针_head->_prev = _head;_head->_next = _head;_size = 0;}

(1) 无参构造

调用Init_List();初始化函数即可.

 list()//初始化很重要(无参构造){Init_List();}

(2) n个value构造

  1. 进行初始化操作.
  2. 尾插nvalue.
	//n个value构造list(int n, const T& value = T()){Init_List();while (n--){push_back(value);}}

(3) 迭代器区间构造

  1. 进行初始化操作.
  2. 利用迭代器的特性,一次将区间中的数据尾插入链表.
//迭代器区间构造template <class Iterator>list(Iterator first, Iterator last){Init_List();while (first != last){push_back(*first);++first;}}

(4) 拷贝构造

链表在物理空间上是不连续的,所以,对于参数是另一个链表的拷贝构造,只能遍历链表进行依次插入数据.

	//拷贝构造list(const list<T>& list){Init_List();auto it = list.begin();while (_size!=list._size){push_back(*it);it++;}}

三、迭代器

迭代器的实现

① 构造

迭代器本质就是一个 Node* _node;结点类的指针.
对于迭代器的构造函数,只需要将结点的地址传过来即可.

list_iterator(Node* node)			//默认构造:_node(node) {}
list_iterator(const self& list)     //拷贝构造:_node(list._node){}

*->

(1) *
*运算符重载,表示对迭代器进行解引用.
使用场景:

list<int>::iterator it = L1.begin();
int start=*it;

很明显,*运算符是需要获取结点所存储的数据.为了减少拷贝以及对数据进行修改,这里采用传引用(Ref )返回.

 Ref operator*() {return _node->_val;//获取该结点的数据}

(2) ->

上面链表中的数据是简单的类型int
在这里插入图片描述

Ptr operator->() {return &_node->_val;// 等价于 return &(_node->_val);}

③ 前置++与后置++

对于链表,迭代器++表示向后访问下一个(后继)结点.学过链表的友友们应该知道.
也就是 _node = _node->_next;

前置++,返回++后的结点的迭代器
后置++,返回++前的结点的迭代器

 //前置++self& operator++() {_node = _node->_next;return *this;}//后置++self operator++(int) {Node* tmp=_node;        //保存++之前的值_node = _node->_next;return tmp;             //返回++之前的值}

④ 前置–与后置–

同理,返回当前结点迭代器的前驱结点.
也就是:_node = _node->_prev;

前置–,返回–后的结点的迭代器
后置–,返回–前的结点的迭代器

 //前置--self& operator--(){_node = _node->_prev;return *this;}//后置--self& operator--(int){Node* tmp = _node;          //保存 -- 之前的值_node = _node->_prev;return tmp;                 //返回 -- 之前的值}

⑤ 比较运算符

比较迭代器是否相等,实际就是比较迭代器所指向的结点是否相等.

 bool operator!=(const self& list) const{return _node != list._node;}bool operator==(const self& list){return _node == list._node;}

list类中的迭代器

在这里插入图片描述

iterator begin(): 返回第一个有效元素位置的迭代器
iterator end(): 返回最后一个有效元素位置的迭代器

	typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;iterator begin(){return _head->_next;//也可以强转一下//return iterator(_head->_next);}iterator end(){return _head;//也可以强转一下// return iterator(_head);}//常属性迭代器const_iterator begin()const{return _head->_next;}const_iterator end()const{return _head;}

front和back

在这里插入图片描述

 T& front(){return _head->_next->_val;//返回值}const T& front()const{return _head->_next->_val;}T& back(){return _head->_prev->_val;}const T& back()const{return _head->_prev->_val;}

四、Modifiers:

其实带头双向循环链表的增删改查较于单链表,更加简单,我们画图分析还是很容易实现的.

(1) insert()

在这里插入图片描述
(图片为博主原创,不得随意截图使用)


特殊情况:这是尾插:
在这里插入图片描述(图片为博主原创,不得随意截图使用)


代码示例

 // 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){//pos.node  而不是pos->nodeNode* newnode = new Node(val);Node* _prev_node = pos._node->_prev;      //pos位置结点的 原前置 结点Node* _cur_node = pos._node;             //pos位置的结点_prev_node->_next = newnode;newnode->_prev = _prev_node;_cur_node->_prev = newnode;newnode->_next = _cur_node;++_size;//有效数据的个数+1.return newnode;}

(2) erase()

在这里插入图片描述

  // 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){Node* _prev_node = pos._node->_prev;             //pos位置结点的 原前置(prev) 结点Node* _next_node = pos._node->_next;            //pos位置结点的 原后置(next) 结点_next_node->_prev = _prev_node;_prev_node->_next = _next_node;delete pos._node;--_size;return _next_node;}

(3) push_back() 和 push_front()

 //尾插//void push_back(const T& val)//{//    Node* newnode = new Node(val);//    Node* _tail_node = _head->_prev;      // 原尾结点//    _tail_node->_next = newnode;//    newnode->_prev = _tail_node;//    _head->_prev = newnode;//    newnode->_next = _head;//    //    ++_size;//有效数据的个数+1.//}//复用insert更加方便void push_back(const T& val){insert(end(),val);//在头结点前面插入,即为尾插}//头插void push_front(const T& val) { insert(begin(), val);}

(4) pop_back() 和 pop_front()

复用即可,不过多介绍了.

  //尾删void pop_back() { erase(--end()); }//头删void pop_front() { erase(begin()); }

(5) clear()

clear:清除list中的有效数据
遍历链表进行依次删除结点,并将size置为0.

  void clear(){iterator it = begin();while (it != end()){it = erase(it);}_size = 0;}

(6) swap()

交换两个链表的成员变量即可.

 void swap(list<T>& list){swap(_head, list._head);swap(_size, list._size);}

结语

看完这篇文章,相信大家对list有了更加深层的理解,对于list的迭代器,它并不像前面的stringvector那种原生指针,而是封装成了类,使得链表的迭代器也可以执行++--等操作,因为迭代器类重载了这些运算符.

今天就分享到这里了,如果觉得有帮助的话,可以给牛牛来一个一键三连吗?谢谢支持!
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

软件定制APP开发步骤分析|小程序

软件定制APP开发步骤分析|小程序 软件定制开发步骤&#xff1a; 1.需求分析&#xff1a; 这是软件定制开发的第一步&#xff0c;也是最关键的一步。在这个阶段&#xff0c;软件开发团队需要与客户进行沟通&#xff0c;了解客户的具体需求和期望。通过讨论和交流&#xff0c;确…

前后端分离管理系统day01---Springboot+MybatisPlus

目录 目录 软件 基础知识 一创建后端项目 注意&#xff1a; 删除多余项 创建测试类 二 加入mybatis-plus依赖支持 1.加入依赖码 2.创建数据库实例/创建用户表/插入默认数据 创建数据库实例 创建表 插入数据 3.配置yml文件 注意&#xff1a;wms01必须是数据库的名字&…

[maven] scopes 管理 profile 测试覆盖率

[maven] scopes & 管理 & profile & 测试覆盖率 这里将一些其他的特性和测试覆盖率&#xff08;主要是 jacoco&#xff09; scopes maven 的 scope 主要就是用来限制和管理依赖的传递性&#xff0c;简单的说就是&#xff0c;每一个 scope 都有其对应的特性&…

WebGL 初始化着色器

目录 前言 初始化着色器的7个步骤 创建着色器对象&#xff08;gl.createShader&#xff08;&#xff09;&#xff09; gl.createShader&#xff08;&#xff09;规范 gl.deleteShader&#xff08;&#xff09;规范 指定着色器对象的代码&#xff08;gl.shaderSource&…

Linux高并发服务器开发第四章:Linux网络编程

1. 网络结构模式 C/S结构 简介 服务器 - 客户机&#xff0c;即 Client - Server&#xff08;C/S&#xff09;结构。C/S 结构通常采取两层结构。服务器负责数据的管理&#xff0c;客户机负责完成与用户的交互任务。客户机是因特网上访问别人信息的机器&#xff0c;服务器则是…

【结构型】代理模式(Proxy)

目录 代理模式(Proxy)适用场景代理模式实例代码&#xff08;Java&#xff09; 代理模式(Proxy) 为其他对象提供一种代理以控制对这个对象的访问。Proxy 模式适用于在需要比较通用和复杂的对象指针代替简单的指针的时候。 适用场景 远程代理 (Remote Proxy) 为一个对象在不同…

10分钟设置免费海外远程桌面

前言 本教程将向您介绍如何使用 Amazon Lightsail 服务的免费套餐轻松搭建属于您的远程桌面。依托于 Amazon 全球可用区&#xff0c;您可以在世界各地搭建符合您配置需求的远程桌面。 本教程需要先拥有亚马逊云科技海外账户。现在注册亚马逊云科技账户可以享受12个月免费套餐…

Python与数据分析--每天绘制Matplotlib库实例图片3张-第1天

目录 1.实例1--Bar color demo 2.实例2--Bar Label Demo 3.实例3--Grouped bar chart with labels 1.实例1--Bar color demo import matplotlib.pyplot as plt # 支持中文 plt.rcParams[font.sans-serif] [SimHei] # 用来正常显示中文标签 plt.rcParams[axes.unicode_minus…

Linux系统编程——网络编程的学习

Linux系统编程学习相关博文 Linux系统编程——文件编程的学习Linux系统编程——进程的学习Linux系统编程——进程间通信的学习Linux系统编程——线程的学习 Linux系统编程——网络编程的学习 一、概述1. TCP/UDP2. 端口号3. 字节序4. Sockt服务器和客户端的开发步骤1. 服务器2…

Vue路由与node.js环境搭建

目录 前言 一.Vue路由 1.什么是spa 1.1简介 1.2 spa的特点 1.3 spa的优势以及未来的挑战 2.路由的使用 2.1 导入JS依赖 2.2 定义两个组件 2.3 定义组件与路径对应关系 2.4 通过路由关系获取路由对象 2.5 将对象挂载到vue实例中 2.6 定义触发路由事件的按钮 2.7 定…

利用cms主题构造木马(CVE-2022-26965)

简介 CVE-2022-26965是Pluck CMS 4.7.16版本存在一个远程shell上传执行漏洞。 攻击者可利用此漏洞通过构造恶意的主题包进行上传并执行&#xff0c;未经授权访问服务器&#xff0c;造成潜在的安全隐患。 过程 1.打开环境&#xff0c;查看源码&#xff0c;发现login.php 2.进…

如何制作一个成功的超市购物小程序

随着互联网的普及和移动支付的便捷性&#xff0c;越来越多的消费者选择在网上购物&#xff0c;这也促使越来越多的商家开始搭建自己的小程序商城。对于超市便利店来说&#xff0c;拥有一个便捷、易用的小程序商城能够吸引更多的消费者&#xff0c;提高销售效率。那么如何快速搭…

Eclipse ABAP ADT 集成详细安装教程

最近看到网上有个源码使用CDS做的&#xff0c;然后看了一下原来还可以用eclipse&#xff0c;趁热打铁&#xff0c;试了一把&#xff0c;最后成功了&#xff0c;中间可能会有一些报错&#xff0c;可以自己慢慢解决&#xff0c;大概就是这样的。 SAP的开发&#xff0c;有三种开发…

[设计模式] 浅谈SOLID设计原则

目录 单一职责原则开闭原则里氏替换原则接口隔离原则依赖倒转原则 SOLID是一个缩写词&#xff0c;代表以下五种设计原则 单一职责原则 Single Responsibility Principle, SRP开闭原则 Open-Closed Principle, OCP里氏替换原则 Liskov Substitution Principle, LSP接口隔离原则 …

快速用Python进行数据分析技巧详解

概要 一些小提示和小技巧可能是非常有用的&#xff0c;特别是在编程领域。有时候使用一点点黑客技术&#xff0c;既可以节省时间&#xff0c;还可能挽救“生命”。 一个小小的快捷方式或附加组件有时真是天赐之物&#xff0c;并且可以成为真正的生产力助推器。所以&#xff0…

华为云,让AI算力入山河

整个2023年&#xff0c;全球科技界都在为大模型沸腾。云计算产业作为AI大模型与产业场景间的最短路径&#xff0c;自然也在大模型浪潮中备受关注。目前阶段&#xff0c;云厂商已经纷纷入局大模型&#xff0c;从多个角度探索大模型带给云计算产业的可能性。 但我们往往会忽略这样…

React进阶

TODO1 组件生命周期 React 组件生命周期 | 菜鸟教程 (runoob.com)https://www.runoob.com/react/react-component-life-cycle.html 什么是组件生命周期 在 React 中&#xff0c;组件生命周期是指组件从创建到销毁期间经历的一系列阶段。在每个阶段&#xff0c;React 给予我…

【力扣每日一题】2023.9.21 收集树中金币

目录 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 代码&#xff1a; 题目&#xff1a; 示例&#xff1a; 分析&#xff1a; 题目给我们一棵树&#xff0c;不过这棵树不是普通的树&#xff0c;而是无向无根树。给我们一个二维数组表示节点之间的连接关系&#xff…

Python —— excel文件操作(超详细)

背景 很多公司还是用excel去管理测试用例的&#xff0c;所以为了减少重复繁琐的导出导出工作&#xff0c;学会如何用代码操作excel表格很实用~ 1、读取excel文件基本步骤 1、操作excel的一些库 1、xlrd&#xff1a;读取库&#xff0c;xlwt&#xff1a;写入&#xff0c;现在…

肖sir___环境的讲解详情__002

一、环境讲解 1、jdk 什么是JDK&#xff1f;JDK的作用&#xff1f; JDK是java语言的软件开发工具包&#xff0c;能解释java程序&#xff0c;编译java语言&#xff0c;没有jdk的话无法编译Java程序。 包含了各种类库和工具&#xff0c;机器不是直接识别语言的&#xff0c;会借助…