C++STL模板之——list(简化源码,模拟源码)

众所周知,C++给我们底层搬砖人提供了很多便捷的数据结构,让我们能偶尔偷懒,list就是其中之一,现在让我们来了解一下它吧

目录

一,原理

1)底层大致结构

2)迭代器

3)模板

二,模拟源码

1)链表结点

2)list类部分

3)迭代器类


一,原理

1)底层大致结构

list底层是由带头双向链表构成的,带头即带哨兵位,双向就是可以从前往后遍历也可从后往前遍历。那这个时候就有人好奇哨兵位指向前一个结点的指针指向哪里?最后一个结点的next指针指向哪里?答案是哨兵位的prv指针指向最后一个结点,最后一个结点的next则指向哨兵位。

2)迭代器

大家有没有发现一个问题,底层是链表,链表的地址是随机的,那迭代器的++不是有问题吗?这是当然的,所有我们需要单独处理迭代器,无法像vector一样的直接++就可拿到下一个元素的地址,那我们应该怎么解决呢?答案是将迭代器单独写一个类,对++,--,等函数进行重载,封装之后就可利用链表里面的指针拿到下一个或者上一个元素的地址,同时begin()和end()返回的就应该是这个迭代器类。

3)模板

因为要适配各种元素,所有这里必须使用模板。而模板的加入必然会让底层的难度和结构更加复杂,同时我们要考虑迭代器类也要加模板以此来适配各种类。

二,模拟源码

1)链表结点

我们要分析链表里面的结构,我们要从需求出发,里面需要什么,首先两个链表指针分别指前和指后,还有应该模板元素用于存储使用者的值,构造函数用于调用初始化,而析构函数建议交给list类。

template<class T>struct ListNode{ListNode(const T& val = T()): _pPre(nullptr), _pNext(nullptr), _val(val){}ListNode<T>* _pPre;//指向前一个结点ListNode<T>* _pNext;//指向后一个结点T _val;             //真正存储的有效值};
2)list类部分

首先我们要考虑,list的私有成员是什么?那肯定是结点啊,从前面的描述可知,list底层结构是带头双向链表,因此私有成员应该是哨兵位指针,由于我们会使用Node* 很多次,因此我们命名为PNode,增加可读性。这里面唯一值得我们注意的就是end()和begin()函数,因为链表底层是不连续的地址,因此我们要将迭代器及其操作封装,begin和end就应该返回被包装的类。还有麻烦的一点就是有些函数需要提供const版本,防止调用的时候找不到匹配的函数。其余的可以按照数据结构链表的基础来写,如果有不懂的可以看我以往的数据结构初阶博客。

//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(){CreateHead();}list(int n, const T& value = T()){CreateHead();for (int i = 0; i < n; ++i)push_back(value);}template <class Iterator>list(Iterator first, Iterator last){CreateHead();while (first != last){push_back(*first);++first;}}list(const list<T>& l){CreateHead();// 用l中的元素构造临时的temp,然后与当前对象交换list<T> temp(l.cbegin(), l.cend());this->swap(temp);}list<T>& operator=(const list<T> l){this->swap(l);return *this;}~list(){clear();delete _pHead;_pHead = nullptr;}///// List Iteratoriterator begin(){return iterator(_pHead->_pNext);}iterator end(){return iterator(_pHead);}const_iterator begin()const{return const_iterator(_pHead->_pNext);}const_iterator end()const{return const_iterator(_pHead);}///// List Capacitysize_t size()const{size_t size = 0;ListNode *p = _pHead->_pNext;while(p != _pHead){size++;p = p->_pNext;}return size;       }bool empty()const{return size() == 0;}// List AccessT& front(){assert(!empty());return _pHead->_pNext->_val;}const T& front()const{assert(!empty());return _pHead->_pNext->_val;}T& back(){assert(!empty());return _pHead->_pPre->_val;}const T& back()const{assert(!empty());return _pHead->_pPre->_val;}// List Modifyvoid push_back(const T& val){insert(end(), val);}void pop_back(){erase(--end());}void push_front(const T& val){insert(begin(), val);}void pop_front(){erase(begin());}// 在pos位置前插入值为val的节点iterator insert(iterator pos, const T& val){PNode pNewNode = new Node(val);PNode pCur = pos._pNode;// 先将新节点插入pNewNode->_pPre = pCur->_pPre;pNewNode->_pNext = pCur;pNewNode->_pPre->_pNext = pNewNode;pCur->_pPre = pNewNode;return iterator(pNewNode);}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){// 找到待删除的节点PNode pDel = pos._pNode;PNode pRet = pDel->_pNext;// 将该节点从链表中拆下来并删除pDel->_pPre->_pNext = pDel->_pNext;pDel->_pNext->_pPre = pDel->_pPre;delete pDel;return iterator(pRet);}void clear(){iterator p = begin();while(p != end()){p = erase(p);}_pHead->_pPrev = _pHead;_pHead->_pNext = _pHead;}void swap(List<T>& l){pNode tmp = _pHead;_pHead = l._pHead;l._pHead = tmp;}private:void CreateHead(){_pHead = new Node;_pHead->_pPre = _pHead;_pHead->_pNext = _pHead;}PNode _pHead;};
}
3)迭代器类

当然,我们首先考虑的还是私有成员,私有成员应该是链表结点,结点不一定是哨兵位,可能是任何一个结点,因为迭代器可以指向任何一个元素,然后就要对迭代器的操作符重写,手动进行地址的加减或者解引用,其中值得人注意的是->操作符重写,很多人可能好奇为什么返回的是结点地址,因为其实a->其实是等价于&a->->,我们返回这个地址便于我们拿到私有成员进行操作。最难理解的一个点是为什么要提供一个ListIterator<T, Ref, Ptr> 的构造函数,因为ListIterator类的模板定义中,有三个模板参数:TRef, 和 Ptr。这三个参数是为了提供更大的灵活性和控制。

 //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):_pNode(pNode){}ListIterator(const Self& l): _pNode(l._pNode){}T& operator*(){return _pNode->_val;}T* operator->(){return &*this;}Self& operator++(){_pNode = _pNode->_pNext;return *this;}Self operator++(int){Self temp(*this);_pNode = _pNode->_pNext;return temp;}Self& operator--(){_pNode = _pNode->_pPre;return *this;}Self& operator--(int){Self temp(*this);_pNode = _pNode->_pPre;return temp;}bool operator!=(const Self& l){return _pNode != l._pNode;}bool operator==(const Self& l){return !(*this!=l);}private:PNode _pNode;};

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

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

相关文章

龙芯安装使用搜狗输入法

CPU&#xff1a;龙芯3A6000 操作系统&#xff1a;Loongnix 桌面主题&#xff1a;Cartoon 龙芯系统切换输入法的按键一般为&#xff1a;Ctrl空格。 1 安装搜狗输入法 进入Loongnix系统自带的龙芯应用合作社&#xff0c;寻找搜狗输入法&#xff0c;点击安装。 按下Ctrl空格&…

计算机网络——网络

计算机网络——网络 小程一言专栏链接: [link](http://t.csdnimg.cn/ZUTXU)前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家&#xff0c; [跳转到网站](https://www.captainbed.cn/qianqiu) 无线网络和移动网…

用HTML5 + JavaScript实现下雪效果

用HTML5 JavaScript实现下雪效果 <canvas>是一个可以使用脚本 (通常为JavaScript) 来绘制图形的 HTML 元素。 <canvas> 标签/元素只是图形容器&#xff0c;必须使用脚本来绘制图形。 HTML5 canvas 图形标签基础https://blog.csdn.net/cnds123/article/details/…

ArcGIS的UTM与高斯-克吕格投影分带要点总结

UTM&#xff08;通用横轴墨卡托投影、等角横轴割椭圆柱投影&#xff09;投影分带投影要点&#xff1a; 1&#xff09;UTM投影采用6度分带 2&#xff09;可根据公式计算&#xff0c;带数&#xff08;经度整数位/6&#xff09;的整数部分31 3&#xff09;北半球地区&#xff0…

蓝桥杯Web应用开发-CSS3 新特性

CSS3 新特性 专栏持续更新中 在前面我们已经学习了元素选择器、id 选择器和类选择器&#xff0c;我们可以通过标签名、id 名、类名给指定元素设置样式。 现在我们继续选择器之旅&#xff0c;学习 CSS3 中新增的三类选择器&#xff0c;分别是&#xff1a; • 属性选择器 • 子…

STM32搭建开发环境

常用开发工具简介 集成开发环境 MDK&#xff1a;全名RealViewMDK&#xff0c;是Keil公司&#xff08;已被ARM收购的&#xff09;一款集成开发环境&#xff0c;界面美观&#xff0c;简单易用&#xff0c;是STM32最常用的集成开发环境EWARM&#xff1a;IAR公司的一款集成开发环…

出现 message: “Request method ‘POST‘ not supported“ 解决方法

目录 1. 问题所示2. 原理分析3. 解决方法1. 问题所示 在执行某个服务器的时候出现如下提示: error: "Method Not Allowed" message: "Request method POST not supported" path: "/oauth/logout" status: 405 timestamp: "2024-02-05 23…

洛谷_P1464 Function_python写法

目录 1.错误解法 2.学习记忆化搜索算法 2.1简介 2.2案例学习 3.解法 4.总结 1.错误解法 a 0 b 0 c 0 def w(a,b,c):if a<0 or b<0 or c<0:return 1elif a>20 or b>20 or c>20:return w(20,20,20)elif a<b and b<c:return w(a-1,b,c) w(a-1,…

Linux命令stress模拟系统负载

stress 是一个在 Linux 下用于模拟系统负载的命令行工具&#xff0c;它可以帮助你测试系统的稳定性以及对系统的负载行为进行评估。下面是 stress 命令的详细使用方法&#xff1a; 安装 stress 在大多数 Linux 发行版中&#xff0c;stress 工具通常不是默认安装的 debian系 …

Vue源码系列讲解——变化侦测篇【下】(Array的变化侦测)

目录 1. 前言 2. 在哪里收集依赖 3. 使Array型数据可观测 3.1 思路分析 3.2 数组方法拦截器 3.3 使用拦截器 4. 再谈依赖收集 4.1 把依赖收集到哪里 4.2 如何收集依赖 4.3 如何通知依赖 5. 深度侦测 6. 数组新增元素的侦测 7. 不足之处 8. 总结 1. 前言 上一篇文…

Angular学习第三天--问题记录

问题一、 1.问题&#xff1a; An unhandled exception occurred: Unexpected token ?? See "C:\Users\22895\AppData\Local\Temp\ng-D60bgy\angular-errors.log" for further details. 2.原因&#xff1a; node版本过低&#xff0c;项目中angular是17&#xff0c;而…

uniapp的api用法大全

页面生命周期API uniApp中的页面生命周期API可以帮助开发者在页面的不同生命周期中执行相应的操作。常用的页面生命周期API包括&#xff1a;onLoad、onShow、onReady、onHide、onUnload等。其中&#xff0c;onLoad在页面加载时触发&#xff0c;onShow在页面显示时触发&#xf…

神经网络 | CNN 与 RNN——深度学习主力军

Hi&#xff0c;大家好&#xff0c;我是半亩花海。本文主要将卷积神经网络&#xff08;CNN&#xff09;和循环神经网络&#xff08;RNN&#xff09;这两个深度学习主力军进行对比。我们知道&#xff0c;从应用方面上来看&#xff0c;CNN 用于图像识别较多&#xff0c;而 RNN 用于…

centos7.9 安装rabbitmq 3.6.15 集群

安装依赖 yum -y install make gcc gcc-c kernel-devel m4 ncurses-devel libxml2-utils libxml2 \ libxslt openssl-devel unixODBC unixODBC-devel unixODBC-bin gtk2 fop wxWidgets-devel wxBaseln -s /usr/bin/wx-config-3.0 /usr/bin/wx-config安装erlang mkdir /opt/er…

计算机网络(第六版)复习提纲26

6 TCP可靠传输的实现 A 以字节为单位的滑动窗口 1 发送窗口 ①发送窗口一定不能超过接收窗口的数值 ②发送窗口后沿后边表示已经发送并确认&#xff0c;后沿只允许前移或不动 ③发送窗口前沿前面表示不可发送&#xff0c;前沿允许不动或前移&#xff0c;也有可能向后收缩&#…

Abap与eCharts

一&#xff0c;简介 利用html与eCharts来绘图&#xff0c;然后用cl_gui_html_viewer将html呈现到abap屏幕中。 二&#xff0c;使用eCharts画图 在一个文件夹中准备如下文件&#xff0c;index.html和echarts.js是必须的&#xff0c;data.json(作为数据源)和jquery.js如果用到就可…

2024年华为OD机试真题-密码解密-Java-OD统一考试(C卷)

题目描述: 给定一段"密文"字符串s,其中字符都是经过"密码本"映射的,现需要将"密文"解密并且输出 映射的规则 (a-i)分别用(1-9)表示;(j-z)分别用(10*-26*)表示 约束:映射始终唯一 输入描述: “密文”字符串 输出描述: 明文字符串 补…

[高性能] - 缓存架构

对于交易系统来说&#xff0c;低延时是核心业务的基本要求。因此需要对业务进行分级&#xff0c;还需要对数据按质量要求进行分类&#xff0c;主要包含两个维度&#xff1a;重要性&#xff0c;延时要求&#xff0c;数据质量。共包含以下三种场景&#xff1a; 1. 重要 延时性要…

Linux系统中安装MAVEN

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

Python程序员面试题精选及解析(2)

本文精心挑选了10道Python程序员面试题&#xff0c;覆盖了Python的多个核心领域&#xff0c;包括装饰器、lambda函数、列表推导式、生成器、全局解释器锁(GIL)、单例模式以及上下文管理器等。每道题都附有简洁的代码示例&#xff0c;帮助读者更好地理解和应用相关知识点无论是对…