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

C++入门之stl六大组件--List源码深度剖析及模拟实现

文章目录

前言

一、List源码阅读

二、List常用接口模拟实现

1.定义一个list节点

2.实现一个迭代器

2.2const迭代器

3.定义一个链表,以及实现链表的常用接口

三、List和Vector

总结


前言

本文中出现的模拟实现经过本地vs测试无误,文件已上传gitee,地址:list: 模仿实现stl的list - Gitee.com


一、List源码阅读

首先我们阅读源码,阅读源码我按照如下方式:先找到单个节点的定义,再找到list里面的主要成员函数。

list_node链表节点的定义,如下:有三个成员,prev,next指针,数据域。

实现链表的接口:增删改查,回想我们用C语言实现的顺序表的时候,使用指针去实现。使用C++的时候,模拟实现string,也是使用迭代器。迭代器就是模拟指针的行为,但是链表直接使用指针++,不一定能访问到下一个节点,所以我们要封装一下原生指针--迭代器,去实现对list的访问。

阅读源码,_list_iterator这个迭代器,有三个模板参数,猜测T应该是类型,Ref是引用,Ptr是指针,为什么有三个模板参数?后续再分析。这个迭代器里实现了一个原生迭代器,一个const迭代器,const调用const对象,然后还有一个self,暂时不明白什么意思。继续往下。

 阅读到这里,重定义了一些参数。注意这里:

  • typedef _list_node<T> * link_type;  
  • link_type node;
  • _list_iterator(link type x):node(x){}

和C语言一样,这里定义了一个节点的指针,作为实现迭代器的最小单元

继续往下阅读,这里有一些运算符重载的函数,比较节点是否相等,以及引用,注意这里引用的返回值使用了reference,上面看出来将Ref重定义成了reference,所以这里可以猜测,ref就是返回引用的值

 这里实现了运算符重载函数,使用迭代器,去访问前后的节点。注意这里的返回值是self,前面读到self就是_list_iterator的一个模板参数。对比之前实现日期类的时候,日期类++,返回的就是一个日期类对象。这里使用迭代器进行++,猜测这里返回的就是一个迭代器。

继续查看迭代器的使用:在list的成员函数中,查看到begin:指向头节点的下一个,end指向头节点。rbegin指向头节点,rend指向头节点的下一个。

判断链表是否为空:判断头节点的下一个是否指向头节点

计算链表的size:根据begin,end去计算

 以及链表中最重要的插入insert,通过迭代器找到pos的位置,根据T去构建一个Node,再进行插入。头插尾插都可以复用Insert。头插头删的时候先保留现在的指针指向情况,再进行修改,修改完了之后再删除这个节点

 往下阅读,到了list的主体部分,有两个模板参数。alloc暂时不明白是什么

 这里实现了一种创建节点的成员函数,create_node:通过T类型的参数x创建一个节点.

empty_initialize 空的初始化,只创建一个节点

二、List常用接口模拟实现

通过上面阅读源码,我们来模仿实现list的一些常用接口。

1.定义一个list节点

template<class T>
struct list_node
{T _data;list_node<T> * _next;list_node<T> * _prev;
}

2.实现一个迭代器

 迭代器要么就是原生指针,要么就是自定义类型对原生指针的封装,模拟指针的行为。

list用一个节点的指针,去构造一个迭代器

template<class T>
struct _list_iterator
{typedef list_node<T> node;typedef _list_iterator<T> self;//定义一个指针,指向链表node * _node;//构造函数 用一个节点指针去构造迭代器_list_iterator(node * n):_node(n){}//实现迭代器的功能//指针++向后遍历,就如日期类,返回的还是一个日期类对象;迭代器++,返回一个迭代器对象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;}T& operator*(){return _ndoe->data;}bool operator == (const self& s){return _node == s._node;}bool operator !=(const self &s){return _node != s._node;}

2.2const迭代器

假设我们传了一个const对象,

  • void print_list(const list<int>&lt)。
  • 对象的类型是一个const list<int> * ,调用不了普通迭代器,是一个经典的权限放大,所以我们要对this加一个const。此时变成了const* _head,指针本身不能改变,但是指向的内容可以改变。即我们还是可以对对象进行改变。
  • 在stl库中,它使用const修饰*this,返回值也是一个const_iterator。但是,对于所有的成员函数都使用const修饰*this和返回值类型很冗余,所以我们这里增加了一个模板参数class Ref。
	template<class T>struct __list_const_iterator{typedef list_node<T> node;typedef __list_const_iterator<T> self;node* _node;__list_const_iterator(node* n):_node(n){}//保护返回的值不能被修改const T& operator*(){return _node->_data;}//... 其他成员函数都相同};
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 * n):_node(n){}Ref operator*(){return _ndoe->data;}//注意这里 如果我定义了一个AA类型的链表,通过迭代器去访问,指针类型为AA*// 现在要访问它的成员 可以这样: *(it)._a1; //也可以it->->a1 一个是运算符重载调用,it是自定义类型,无法直接使用箭头,it-> 就相当于运算符重载operator-> AA*//一个是找成员 //这里为了增强可读性 省略了一个箭头 it->_a1;Ptr operator->(){return &_node->data;}self& operator--(){}//其余运算符操作类似
}template<class T>
class list
{//注意这里模板参数 调用普通迭代器 T&传给ref, 调用const迭代器 const T& 传给ref typedef _list_iterator<T,T&,T*> iterator;typedef _list_iterator<T,const T&, const T*> const_iterator;iterator begin(){return iterator(_head->_next);}const iterator begin(){return const_iterator(_head->_next);}//..其余类似
}

3.定义一个链表,以及实现链表的常用接口

template<class T>
struct _list
{typedef list_node<T> node;typedef _list_iterator<T> iterator;//list的成员node* _head;//list的构造 初始只有一个头节点list(){_head = new node;_head->_next = _head;_head->_prev = _head;}//list的一些成员函数 //通过迭代器定位begin和enditerator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}//通过迭代器对指定pos位置进行增删改查void  insert(iterator pos, const T& x){node new_node = new node(x);//iterator cur = pos;//这里是要通过pos的指针,找到这个节点 对pos进行解引用node * cur = pos._node;node * prev = cur->_prev;prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}void push_back(){insert(end(),x);}void push_front(){insert(begin(),x);}void erase(iterator pos){assert(pos!=end());node * cur = pos._node;node* prev = cur->_prev;node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;}void pop_back(){erase(end());}void pop_front(){erase(begin());}//打印void print_list(const list<T> & lt){iterator it = lt.begin();while(it != lt.end()){cout<<*it;++it;}cout<<endl;}}

三、List和Vector对比

vector与list都是stl中非常重要的序列容器,由于两个容器的底层结构不同,导致特性以及应用场景不同

vectorlist
底层结构动态顺序表,一段连续的空间带头节点的双向循环链表
随机访问支持随机访问,访问某个元素效率O(1)不支持随机访问,访问某个元素效率O(N)
插入和删除在任意位置插入删除元素效率较低,时间复杂度O(N),插入可能需要扩容,开辟新空间,拷贝元素,释放旧空间任意位置插入和删除效率高,不需要搬移元素。时间复杂度O(1)
空间利用率底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低
迭代器原生指针对原生指针(节点指针)进行封装
迭代器失效在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能导致扩容,导致原来迭代器失效,删除时,也可能失效。需要重新给迭代器赋值插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,因为那个节点已经被删除。其他迭代器不受影响
使用场景需要高效存储,支持随机访问,不关心插入删除效率大量插入和删除操作,不关心随机访问

总结

本文主要对stl源码中list内容进行阅读,并模拟实现。技术有限,如有错误请指正。

相关文章:

C++入门之stl六大组件--List源码深度剖析及模拟实现

文章目录 前言 一、List源码阅读 二、List常用接口模拟实现 1.定义一个list节点 2.实现一个迭代器 2.2const迭代器 3.定义一个链表&#xff0c;以及实现链表的常用接口 三、List和Vector 总结 前言 本文中出现的模拟实现经过本地vs测试无误&#xff0c;文件已上传gite…...

windows下以指定用户访问SMB服务器进行读写

一 概述 最近遇到一个问题&#xff0c;linux 的 smb服务器开启匿名访问&#xff0c;windows访问linux文件夹不需要用户名密码就可以进去使用&#xff0c;但是存在一个问题&#xff0c;ssh连接到linux 后修改的文件&#xff0c;在windows已smb方式下打开某个文件修改 是没有权限…...

数组根据属性去重

利用reduce函数处理&#xff0c;直接上代码&#xff01; let data [{name:晓明,id:1},{name:德华,id:2},{name:德华,id:2},{name:晓明,id:1},] var obj {}; let arr data.reduce(function (item, next) {obj[next.id] ? : obj[next.id] true && item.push(next)…...

无损音乐从哪找?五个网站+免费下载,你确定不来看看?

hi&#xff0c;大家好我是技术苟&#xff0c;每天晚上22点准时上线为你带来实用黑科技&#xff01;由于公众号改版&#xff0c;现在的公众号消息已经不再按照时间顺序排送了。因此小伙伴们就很容易错过精彩内容。喜欢黑科技的小伙伴&#xff0c;可以将黑科技百科公众号设为标星…...

2023华数杯数学建模B题思路模型论文分析

目录 一.2023华数杯数学建模最新思路&#xff1a;比赛开始后第一时间更新 更新查看文末名片 二.华数杯简介 三.往年华数杯赛题简介分析&#xff1a; 一.2023华数杯数学建模最新思路&#xff1a;比赛开始后第一时间更新 更新查看文末名片 二.华数杯简介 华数杯简介 国赛前…...

K8S系列文章之 使用Kind部署K8S 并发布服务

简单介绍 kind 即 Kubernetes In Docker&#xff0c;顾名思义&#xff0c;就是将 k8s 所需要的所有组件&#xff0c;全部部署在一个docker容器中&#xff0c;是一套开箱即用的 k8s 环境搭建方案。使用 kind 搭建的集群无法在生产中使用&#xff0c;但是如果你只是想在本地简单…...

从0到1开发go-tcp框架【4实战片— — 开发MMO之玩家聊天篇】

从0到1开发go-tcp框架【实战片— — 开发MMO】 MMO&#xff08;MassiveMultiplayerOnlineGame&#xff09;&#xff1a;大型多人在线游戏&#xff08;多人在线网游&#xff09; 1 AOI兴趣点的算法 游戏中的坐标模型&#xff1a; 场景相关数值计算 ● 场景大小&#xff1a; 250…...

无重复字符的最长子串 LeetCode热题100

题目 给定一个字符串 s &#xff0c;请你找出其中不含有重复字符的 最长连续子字符串 的长度。 思路 使用滑动窗口&#xff0c;记录窗口区间的长度大小&#xff0c;取最大值。用map存储滑动窗口内所有字符&#xff0c;字符作为key&#xff0c;每个字符的数量作为value。遍历…...

Docker搭建zookeeper

问题背景 前言 本文参考自&#xff1a;docker-compose快速搭建Zookeeper集群还有一种更加详细更加全面的部署方式&#xff1a;Docker之docker-compose一键部署Zookeeper集群&#xff0c;但笔者还未验证&#xff0c;先记录下来 搭建 安装docker-ce 此处不赘述 安装docker-co…...

LeetCode 热题 100 JavaScript--160. 相交链表

/*** Definition for singly-linked list.* function ListNode(val) {* this.val val;* this.next null;* }*//*** param {ListNode} headA* param {ListNode} headB* return {ListNode}*/// 1、暴力解法 var getIntersectionNode function(headA, headB) {var p1 …...

AWS S3 协议对接 minio/oss 等

使用亚马逊 S3 协议访问对象存储 [s3-API](https://docs.aws.amazon.com/zh_cn/AmazonS3/latest/API/API_Operations_Amazon_Simple_Storage_Service.html)- 兼容S3协议的对象存储有- minio- 似乎是完全兼容 [兼容文档](https://www.minio.org.cn/product/s3-compatibility.htm…...

手机便签内容不见了怎么恢复正常?

在日常生活和工作中&#xff0c;很多人都需要随手记录事情&#xff0c;例如家庭琐事、孩子相关的事情、指定时间需要完成的工作任务、会议安排等。当我们需要随时随地记录事情的时候&#xff0c;手机便签应用就是非常不多的选择&#xff0c;我们直接打开手机上的便签APP就可以新…...

【架构】Java 系统架构演进的思考

文章目录 1 前言2 单体应用架构3 垂直应用架构4 分布式架构5 SOA 架构6 微服务云架构7 总结 1 前言 随着移动互联的发展&#xff0c;网站、H5、移动端的应用规模也不断扩大&#xff0c;不管是应用的数量还是质量都得到了指数级的提升。开发者的数量与日俱增&#xff0c;应用的…...

Python爬虫——解析_jsonpath

jsonpath的安装 pip install jsonpathjsonpath的使用&#xff1a; obj json.load(open(json文件, r, encodingutf-8)) ret jsonpath.jsonpath(obj, jsonpath语法)json文件&#xff1a; { "store": {"book": [{ "category": "末世"…...

华为发布数字资产继承功能

在华为开发者大会2023&#xff08;HDC.Together&#xff09;上&#xff0c;华为常务董事、终端BG CEO、智能汽车解决方案BU CEO余承东正式发布了数字资产继承功能&#xff0c;HarmonyOS提供了安全便捷的数字资产继承路径。 在鸿蒙世界中&#xff0c;我们每个人在每台设备、应用…...

阿里云NAS文件存储基本介绍与购买使用

文章目录 1.NAS文件存储基本概念1.1.什么是NAS文件存储1.2.NAS的应用场景1.3.NAS、OSS、EBS的区别 2.购买NAS文件存储2.1.开通NAS服务2.2.创建NAS文件系统2.3.配置NAS文件系统属性2.4.查看购买的NAS服务 3.NAS文件存储基本使用3.1.修改NAS文件系统默认的名称3.2.NAS的权限组3.3…...

大模型使用——超算上部署LLAMA-2-70B-Chat

大模型使用——超算上部署LLAMA-2-70B-Chat 前言 1、本机为Inspiron 5005&#xff0c;为64位&#xff0c;所用操作系统为Windos 10。超算的操作系统为基于Centos的linux&#xff0c;GPU配置为A100&#xff0c;所使用开发环境为Anaconda。 2、本教程主要实现了在超算上部署LLAM…...

机器学习笔记:李宏毅ChatGPT课程1:刨析ChatGPT

ChatGPT——Chat Generative Pre-trained Transformer 1 文字接龙 每次输出一个概率分布&#xff0c;根据概率sample一个答案 ——>因为是根据概率采样&#xff0c;所以ChatGPT每次的答案是不一样的&#xff08;把生成式学习拆分成多个分类问题&#xff09;将生成的答案加到…...

Llama 2 with langchain项目详解(三)

Llama 2 with langchain项目详解(三) 17.3 Llama 2 with langchain基础 本节讲解在LangChain中使用Llama 2模型的基础知识,展示如何运行LangChain的代码,及在云端运行Llama 2的700亿模型。 首先,使用Python的pip管理器安装一系列库,包括huggingface/transformers、datase…...

牛客 AB30 排序(快排模板)

描述 给定一个长度为 n 的数组&#xff0c;请你编写一个函数&#xff0c;返回该数组按升序排序后的结果。 数据范围&#xff1a; 0≤&#xfffd;≤11030≤n≤1103&#xff0c;数组中每个元素都满足 0≤&#xfffd;&#xfffd;&#xfffd;≤1090≤val≤109 要求&#xff1…...

【Linux旅行记】第一个小程序“进度条“!

文章目录 一、预备知识1.1回车换行1.2缓冲区 二、倒计时三、进度条3.1普通版本源代码3.2高级版本源代码 &#x1f340;小结&#x1f340; &#x1f389;博客主页&#xff1a;小智_x0___0x_ &#x1f389;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &…...

DeepMind将AI用于可控核聚变:将等离子体形状模拟精度提高65%

近日&#xff0c;英国AI公司DeepMind宣布取得了一项新的突破&#xff0c;成功实现了AI可控核聚变。这一技术能够在高温等离子体环境下实现精准放电&#xff0c;为核聚变技术的发展提供了新的思路和创新。 长期以来&#xff0c;相关领域的科学家们&#xff0c;一直在寻找清洁、取…...

Scrum是什么意思,Scrum敏捷项目管理工具有哪些?

一、什么是Scrum&#xff1f; Scrum是一种敏捷项目管理方法&#xff0c;旨在帮助团队高效地开展软件开发和项目管理工作。 Scrum强调迭代和增量开发&#xff0c;通过将项目分解为多个短期的开发周期&#xff08;称为Sprint&#xff09;&#xff0c;团队可以更好地应对需求变…...

【从零单排Golang】第十三话:使用WaitGroup等待多路并行的异步任务

在后端开发当中&#xff0c;经常会遇到这样的场景&#xff1a;请求给了批量的输入&#xff0c;对于每一个输入&#xff0c;我们都要给外部发请求等待返回&#xff0c;然后才能继续其它自己的业务逻辑。在这样的case下&#xff0c;如果每一个输入串行处理的话&#xff0c;那么很…...

WSL2安装CentOS7和CentOS8

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、下载ZIP包&#xff1f;二、安装1.打开Windows子系统支持2.安装到指定位置3.管理虚拟机4.配置虚拟机1.配置国内源2.安装软件3.安装第三方源 5.配置用户1.创建…...

不平衡电网条件下基于变频器DG操作的多目标优化研究(Matlab代码Simulink实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f308;4 Matlab代码&Simulink实现&文章讲解 &#x1f4a5;1 概述 文献来源&#xff1a; 最近&#xff0c;利用并网转换器&#xff08;GCC&#xff09;克服电网故障并支撑电网电压已…...

【Leetcode】(自食用)简单题||单词数

step by step. 题目&#xff1a; 统计字符串中的单词个数&#xff0c;这里的单词指的是连续的不是空格的字符。 请注意&#xff0c;你可以假定字符串里不包括任何不可打印的字符。 示例: 输入: "Hello, my name is John" 输出: 5 解释: 这里的单词是指连续的不是空格…...

C语言代码的x86-64汇编指令分析过程记录

先通过Xcode创建一个terminal APP&#xff0c;语言选择C。代码如下&#xff1a; #include <stdio.h>int main(int argc, const char * argv[]) {int a[7]{1,2,3,4,5,6,7};int *ptr (int*)(&a1);printf("%d\n",*(ptr));return 0; } 在return 0处打上断点&…...

基于springboot+vue的房屋租赁系统(前后端分离)

博主主页&#xff1a;猫头鹰源码 博主简介&#xff1a;Java领域优质创作者、CSDN博客专家、公司架构师、全网粉丝5万、专注Java技术领域和毕业设计项目实战 主要内容&#xff1a;毕业设计(Javaweb项目|小程序等)、简历模板、学习资料、面试题库、技术咨询 文末联系获取 项目介绍…...

Python文件读写操作详解:从基础到高级

摘要&#xff1a;文件读写是Python编程中常见的操作之一。本文将介绍Python中文件读写的基础知识&#xff0c;包括打开文件、读取文件内容、写入文件、关闭文件等基本操作。此外&#xff0c;还将探讨一些高级文件读写技术&#xff0c;如使用上下文管理器、处理异常、使用with语…...