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

C++第十二弹 -- STL之list模拟实现

文章索引

  • 前言
  • 模拟实现list
    • 1. ListNode节点类
    • 2. list的迭代器封装
    • 3. 反向迭代器
    • 4. list类的模拟实现
    • 测试代码
  • list的反向迭代器
  • 总结

前言

通过模拟实现可以让我们更加深刻的理解C++底层STL的实现逻辑, 本篇就对list的底层进行模拟实现.

博客主页: 酷酷学!!! 点击关注 共同进步!!!


正文开始

模拟实现list

要模拟实现list, 必须要熟悉list的底层结构以及其接口的含义, 通过上一篇的学习, 这些内容基本掌握之后, 现在我们来模拟实现list.

1. ListNode节点类

#pragma once
#include<iostream>
using namespace std;
#include<assert.h>namespace my
{//list的节点类template<class T>struct ListNode{ListNode(const T& val = T()): _prev(nullptr), _next(nullptr), _val(val){}ListNode<T>* _prev;ListNode<T>* _next;T _val;};

2. list的迭代器封装

list的迭代器
迭代器有两种实现方式, 具体应根据容器底层数据结构的实现:
1.原生态指针, 比如:vector
2.将原生态指针进行封装, 因迭代器使用形式与指针完全相同,
因此在自定义的类中必须实现以下方法:1.指针可以解引用,迭代器的类中必须重载operator*()2.指针可以通过->访问其所指空间成员,迭代器类中必须重载operator->()3.指针可以++向后移动,迭代器类中必须重载operator++()与operator++(int)至于operator--()/operator--(int)释放需要重载,根据具体的结构来抉择,双向链表可以向前或者向后移动,所以需要重载,如果是forward_list就不需要重载--4.迭代器需要进行是否相等的比较,因此还需要重载operator==()与operator!=()
	template<class T,class Ref,class Ptr>class ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;//Ref 和 Ptr类型需要重新定义下,实现反向迭代器时需要用到public:typedef Ref Ref;typedef Ptr Ptr;//构造ListIterator(Node* node = nullptr): _node(node){}//具有指针类似行为Ref operator*(){return _node->_val;}Ptr operator->(){return &(operator*());}//迭代器支持移动Self& operator++(){_node = _node->_next;return *this;}Self operator++(int){Self temp(*this);_node = _node->_next;return temp;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self temp(*this);_node = _node->_prev;return temp;}//迭代器支持比较bool operator!=(const Self& l) const{return _node != l._node;}bool operator==(const Self & l) const{return _node == l._node;}Node* _node;};

3. 反向迭代器

	//反向迭代器,迭代器复用template<class Iterator>class ReverseListIterator{//注意:此处typename的作用是明确的告诉编译器,Ref是Iterator类中的一个类型,//而不是静态成员变量,否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员//变量,因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的public:typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef ReverseListIterator<Iterator> Self;//构造ReverseListIterator(Iterator it): _it(it){}//具有指针类似行为Ref operator*(){Iterator temp(_it);--temp;return *temp;}Ptr operator->(){return &(operator*());}//迭代器支持移动Self& operator++(){--_it;return *this;}Self& operator++(int){Self temp(*this);--_it;return *this;}Self& operator--(){++_it;return *this;}Self& operator--(int){Self temp(*this);++_it;return temp;}//迭代器支持比较bool operator!=(const Self& l) const{return _it != l._it;}bool operator==(const Self& l) const{return _it == l._it;}Iterator _it;};

4. list类的模拟实现

	template<class T>class list{typedef ListNode<T> Node;public://正向迭代器typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, T*> const_iterator;//反向迭代器typedef ReverseListIterator<iterator> reverse_iterator;typedef ReverseListIterator<const_iterator> const_reverse_iterator;//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.begin(), l.end());this->swap(temp);}list<T>& operator=(list<T> l){this->swap(l);return *this;}~list(){clear();delete _head;_head = nullptr;}//list的迭代器iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}//List的容器相关size_t size() const{Node* cur = _head->_next;size_t count = 0;while (cur != _head){++count;cur = cur->_next;}return count;}bool empty() const{return _head->_next = _head;}void resize(size_t newsize, const T* data = T()){size_t oldsize = size();if (newsize <= oldsize){//将有效元素减少到newsizewhile (newsize < oldsize){pop_back();--oldsize;}}else{while (oldsize < newsize){push_back(data);++oldsize;}}}//list的元素访问操作//注意:List不支持operator[]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;}//list的插入和删除void 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){Node* pNewNode = new Node(val);Node* pCur = pos._node;//先将新节点插入pNewNode->_prev = pCur->_prev;pNewNode->_next = pCur;pNewNode->_prev->_next = pNewNode;pCur->_prev = pNewNode;return iterator(pNewNode);}//删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){//找到待删除的节点Node* pDel = pos._node;Node* pRet = pDel->_next;//将该节点从链表中拆下来并删除pDel->_prev->_next = pDel->_next;pDel->_next->_prev = pDel->_prev;delete pDel;return iterator(pRet);}void clear(){Node* cur = _head->_next;//采用头删除删除while (cur != _head){Node* next = cur->_next;delete cur;cur = next;}_head->_next = _head->_prev = _head;}void swap(my::list<T>& l){std::swap(_head, l._head);}private:void CreateHead(){_head = new Node;_head->_prev = _head;_head->_next = _head;}Node* _head;};	
}

测试代码

对构造函数进行测试

//对模拟实现的list进行测试
//正向打印链表
template<class T>
void PrintList(const my::list<T>& l)
{auto it = l.begin();while (it != l.end()){cout << *it << " ";++it;}cout << endl;
}//测试list的构造
void Testmylist1()
{my::list<int> l1;my::list<int> l2(10, 5);PrintList(l2);int array[] = { 1,2,3,4,5,6,7,8,9,0 };my::list<int> l3(array, array + sizeof(array) / sizeof(array[0]));PrintList(l3);my::list<int> l4(l3);PrintList(l4);l1 = l4;PrintList(l1);
}

在这里插入图片描述

对头/尾插入删除进行测试

//PushBack()/PopBack()/PushFront()/PopFront()
void Testmylist2()
{//测试pushBack和PopBackmy::list<int> l;l.push_back(1);l.push_back(2);l.push_back(3);PrintList(l);l.pop_back();l.pop_back();PrintList(l);l.pop_back();cout << l.size() << endl;//测试pushFront与popFrontl.push_front(1);l.push_front(2);l.push_front(3);PrintList(l);l.pop_front();l.pop_front();PrintList(l);l.pop_front();cout << l.size() << endl;
}

在这里插入图片描述

测试指定位置插入删除

//测试insert和erase
void Testmylist3()
{int array[] = { 1,2,3,4,5 };my::list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto pos = l.begin();l.insert(l.begin(), 0);PrintList(l);++pos;l.insert(pos, 2);PrintList(l);l.erase(l.begin());l.erase(pos);PrintList(l);//pos指向的节点已经被删除,pos迭代器失效cout << *pos << endl;auto it = l.begin();while (it != l.end()){it = l.erase(it);}cout << l.size() << endl;
}

在这里插入图片描述

测试反向迭代器

//测试反向迭代器
void Testmylist4()
{int array[] = { 1,2,3,4,5 };my::list<int> l(array, array + sizeof(array) / sizeof(array[0]));auto rit = l.rbegin();while (rit != l.rend()){cout << *rit << " ";++rit;}cout << endl;const my::list<int> cl(l);auto crit = l.rbegin();while (crit != l.rend()){cout << *crit <<" ";++crit;}cout << endl;
}

在这里插入图片描述

list的反向迭代器

我们知道, 反向迭代器的++就是正向迭代器的- -, 反向迭代器的- -就是正向迭代器的++, 因此反向迭代器的实现可以借助正向迭代器, 即: 返现迭代器内部可以包含一个正向迭代器, 对正向迭代器的接口进行包装即可.

	//反向迭代器,迭代器复用template<class Iterator>class ReverseListIterator{//注意:此处typename的作用是明确的告诉编译器,Ref是Iterator类中的一个类型,//而不是静态成员变量,否则编译器编译时就不知道Ref是Iterator中的类型还是静态成员//变量,因为静态成员变量也是按照 类名::静态成员变量名 的方式访问的public:typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef ReverseListIterator<Iterator> Self;//构造ReverseListIterator(Iterator it): _it(it){}//具有指针类似行为Ref operator*(){Iterator temp(_it);--temp;return *temp;}Ptr operator->(){return &(operator*());}//迭代器支持移动Self& operator++(){--_it;return *this;}Self& operator++(int){Self temp(*this);--_it;return *this;}Self& operator--(){++_it;return *this;}Self& operator--(int){Self temp(*this);++_it;return temp;}//迭代器支持比较bool operator!=(const Self& l) const{return _it != l._it;}bool operator==(const Self& l) const{return _it == l._it;}Iterator _it;};

总结

通过以上的实现,可以模拟出一个类似于list的数据结构,并且可以对其中的元素进行添加、删除、查找、等操作。这样就可以在不使用C++内置的list时,使用自己实现的List类来进行相同的操作。

感谢您的点赞与收藏!!!

相关文章:

C++第十二弹 -- STL之list模拟实现

文章索引 前言模拟实现list1. ListNode节点类2. list的迭代器封装3. 反向迭代器4. list类的模拟实现测试代码 list的反向迭代器总结 前言 通过模拟实现可以让我们更加深刻的理解C底层STL的实现逻辑, 本篇就对list的底层进行模拟实现. 博客主页: 酷酷学!!! 点击关注 共同进步!…...

Destiny of Gods首轮测试正式开启,参与玩家数量突破10万

天神风云&#xff0c;波澜再兴&#xff0c;GameFi链游聚合平台Destiny of Gods首款同名数字卡牌回合制游戏首轮测试定档8月20日20:00&#xff08;GMT8&#xff09;&#xff0c;现已正式开启&#xff01; 这是一个由人、游灵和神灵共存的世界&#xff0c;历经蛮荒时期的纷争与信…...

QT聊天室基于Tcp

server.cpp #include "widget.h" #include "ui_widget.h"Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget),server(new QTcpServer(this)) // 给服务器指针对象实例化空间{ui->setupUi(this); }Widget::~Widget() {delete ui; }…...

公开课观后感:密歇根大学python for everyone

从2024年1月17日到2024年8月20日&#xff0c;终于将密歇根大学的python for everyone的python公开课跟完。站在一月份规划的时刻来看&#xff0c;比我想象中花费的时间更多&#xff0c;我当时肯定没有想到要花上整整七个月的时间才能将这个公开课的内容看完&#xff0c;毕竟这个…...

goweb框架-gin

文章目录 Gin框架概览Gin框架的特点Gin框架的安装和基本使用安装基本使用 路由系统路由的基本概念Gin框架路由的特点 Radix Tree&#xff08;基数树&#xff09;基数树的定义和原理基数树在Gin框架中的应用节省空间的优化动态路由和通配符处理 路由树的构建注册路由的过程路由树…...

2024年接口测试高频面试题及答案

1. 什么是接口测试&#xff1f; •接口测试就是通过测试不同情况下的入参与之相应的出参信息来判断接口是否符合或满足相应的功能性、安全性要求 •测试的重点是要检查数据的交换&#xff0c;传递和控制管理过程&#xff0c;以及系统间的相互逻辑依赖关系 2. 为什么要做接口…...

ESP32-C3在MQTT访问时出现“transport_base: Poll timeout or error”问题的分析(8)

接前一篇文章:ESP32-C3在MQTT访问时出现“transport_base: Poll timeout or error”问题的分析(7) 前边几回分析了笔者在MQTT测试时所遇到的问题: 最终定位到了是由于components\components\tcp_transport\transport_ssl.c的base_poll_write函数中调用的select函数超时返回…...

Linux: 忘记密码的解决方法,passwd

https://www.redhat.com/sysadmin/recover-root-passwd 这里的方法很简单&#xff0c;就是通过console进去&#xff0c;添加一个启动参数&#xff0c;加载sysroot&#xff0c;然后用passwd命令修改密码。这个是RHEL7适用。 https://access.redhat.com/solutions/1192 这个是提…...

36. 有效的数独【 力扣(LeetCode) 】

一、题目描述 请你判断一个 9 x 9 的数独是否有效。只需要 根据以下规则 &#xff0c;验证已经填入的数字是否有效即可。 数字 1-9 在每一行只能出现一次。 数字 1-9 在每一列只能出现一次。 数字 1-9 在每一个以粗实线分隔的 3x3 宫内只能出现一次。&#xff08;请参考示例图…...

机器学习中的没有免费午餐定理

嘿&#xff0c;各位机器学习的爱好者们&#xff01;今天&#xff0c;让我们一起深入探讨机器学习中那个神秘而又重要的概念——没有免费午餐定理。 一、定理引入&#xff1a;探索算法森林的钥匙 在广阔无垠的机器学习领域中&#xff0c;免费午餐定理就如同一把神奇的钥匙&…...

高级java每日一道面试题-2024年8月21日-框架篇[Spring篇]-使用IOC容器应该注意哪些?

如果有遗漏,评论区告诉我进行补充 面试官: 使用IOC容器应该注意哪些? 我回答: 1. 理解IOC的基本概念 控制反转&#xff1a;在传统的编程模式中&#xff0c;程序会主动控制依赖关系的创建和管理。而在IoC容器中&#xff0c;这种控制权被反转给了容器本身。程序员只需要声明…...

LLM训练推理相关概念

1. 有监督微调&#xff08;Supervised Fine-Tuning&#xff09;与指令微调&#xff08;Instruction Fine-Tuning&#xff09;对模型参数的影响 **有监督微调&#xff08;Supervised Fine-Tuning, SFT&#xff09;和指令微调&#xff08;Instruction Fine-Tuning, Instruct-Tun…...

IP in IP 协议

IP in IP 是一种多重IP协议&#xff0c;即&#xff1a;客户机可以发送一个IP协议内部在嵌套一个IP协议到某个特定的主机上&#xff0c;在由具体的主机作为路由进行转发的协议。 例如&#xff1a; IP in IP帧协议结构为&#xff0c;第一层为发送到IP in IP 路由主机的报文&…...

DAY2: HTTP请求报文和响应报文是怎样的,有哪些常见的字段?| HTTP有哪些请求方式?| GET请求和POST请求的区别

目录 HTTP请求报文和响应报文是怎样的&#xff0c;有哪些常见的字段&#xff1f; 请求报文 响应报文 HTTP有哪些请求方式&#xff1f; GET请求和POST请求的区别 HTTP请求报文和响应报文是怎样的&#xff0c;有哪些常见的字段&#xff1f; HTTP报文分为请求报文和响应报文…...

线性代数:每日一题1/特征值与相似对角化

设A, B 为二阶矩阵&#xff0c;且 AB BA , 则“A有两个不相等的特征值”是“B可对角化"的&#xff08;&#xff09; A. 充分必要条件 B. 充分不必要条件 C.必要不充分条件 D.既不充分也不必要条件 知识点&#xff1a; 特征向量与特征值的关系 相似矩阵的定义和性质 n阶…...

Android UI:PopupWindow:API

文章目录 类操作 对PopupWindow的操作 创建PopupWindow对象的操作添加并显示PopupWindow的操作移除PopupWindow的操作更新PopupWindow的操作显示内容的相关操作 布局的相关操作进入退出动画的相关操作 Transition设置进入动画的相关操作Transition设置退出动画的相关操作XML设置…...

什么是DevUI?

DevUI是面向企业中后台产品的开源前端解决方案&#xff0c;其设计价值观基于"高效、开放、可信、乐趣"四种自然与人文相结合的理念&#xff0c;旨在为设计师、前端开发者提供标准的设计体系&#xff0c;并满足各类落地场景&#xff0c;是一款企业级开箱即用的产品。 …...

DAY53

作业&#xff1a; 运行1个服务器和2个客户端 实现效果&#xff1a; 服务器和2个客户端互相聊天&#xff0c;服务器和客户端都需要使用select模型去实现 服务器要监视2个客户端是否连接&#xff0c;2个客户端是否发来消息以及服务器自己的标准输入流 客户端要监视服务器是否发来…...

python中len是什么

Python len() 方法返回字符串长度。 len()方法语法&#xff1a; len( str ) 返回值&#xff1a; 返回字符串长度。 以下实例展示了len()的使用方法&#xff1a; #!/usr/bin/python str "this is string example....wow!!!"; print "字符串长度: ", len…...

推荐一个开源的kafka可视化客户端GUI工具(Kafka King)

大佬的博客地址&#xff1a; https://blog.ysboke.cn/posts/tools/kafka-king Github地址&#xff1a; https://github.com/Bronya0/Kafka-King Kafka-King功能清单 查看集群节点列表&#xff08;完成&#xff09;支持PLAINTEXT、SASL PLAINTEXT用户名密码认证&#xff08;完…...

智慧医疗能源事业线深度画像分析(上)

引言 医疗行业作为现代社会的关键基础设施,其能源消耗与环境影响正日益受到关注。随着全球"双碳"目标的推进和可持续发展理念的深入,智慧医疗能源事业线应运而生,致力于通过创新技术与管理方案,重构医疗领域的能源使用模式。这一事业线融合了能源管理、可持续发…...

基于ASP.NET+ SQL Server实现(Web)医院信息管理系统

医院信息管理系统 1. 课程设计内容 在 visual studio 2017 平台上&#xff0c;开发一个“医院信息管理系统”Web 程序。 2. 课程设计目的 综合运用 c#.net 知识&#xff0c;在 vs 2017 平台上&#xff0c;进行 ASP.NET 应用程序和简易网站的开发&#xff1b;初步熟悉开发一…...

uni-app学习笔记二十二---使用vite.config.js全局导入常用依赖

在前面的练习中&#xff0c;每个页面需要使用ref&#xff0c;onShow等生命周期钩子函数时都需要像下面这样导入 import {onMounted, ref} from "vue" 如果不想每个页面都导入&#xff0c;需要使用node.js命令npm安装unplugin-auto-import npm install unplugin-au…...

服务器硬防的应用场景都有哪些?

服务器硬防是指一种通过硬件设备层面的安全措施来防御服务器系统受到网络攻击的方式&#xff0c;避免服务器受到各种恶意攻击和网络威胁&#xff0c;那么&#xff0c;服务器硬防通常都会应用在哪些场景当中呢&#xff1f; 硬防服务器中一般会配备入侵检测系统和预防系统&#x…...

NFT模式:数字资产确权与链游经济系统构建

NFT模式&#xff1a;数字资产确权与链游经济系统构建 ——从技术架构到可持续生态的范式革命 一、确权技术革新&#xff1a;构建可信数字资产基石 1. 区块链底层架构的进化 跨链互操作协议&#xff1a;基于LayerZero协议实现以太坊、Solana等公链资产互通&#xff0c;通过零知…...

NXP S32K146 T-Box 携手 SD NAND(贴片式TF卡):驱动汽车智能革新的黄金组合

在汽车智能化的汹涌浪潮中&#xff0c;车辆不再仅仅是传统的交通工具&#xff0c;而是逐步演变为高度智能的移动终端。这一转变的核心支撑&#xff0c;来自于车内关键技术的深度融合与协同创新。车载远程信息处理盒&#xff08;T-Box&#xff09;方案&#xff1a;NXP S32K146 与…...

基于 TAPD 进行项目管理

起因 自己写了个小工具&#xff0c;仓库用的Github。之前在用markdown进行需求管理&#xff0c;现在随着功能的增加&#xff0c;感觉有点难以管理了&#xff0c;所以用TAPD这个工具进行需求、Bug管理。 操作流程 注册 TAPD&#xff0c;需要提供一个企业名新建一个项目&#…...

搭建DNS域名解析服务器(正向解析资源文件)

正向解析资源文件 1&#xff09;准备工作 服务端及客户端都关闭安全软件 [rootlocalhost ~]# systemctl stop firewalld [rootlocalhost ~]# setenforce 0 2&#xff09;服务端安装软件&#xff1a;bind 1.配置yum源 [rootlocalhost ~]# cat /etc/yum.repos.d/base.repo [Base…...

PHP 8.5 即将发布:管道操作符、强力调试

前不久&#xff0c;PHP宣布了即将在 2025 年 11 月 20 日 正式发布的 PHP 8.5&#xff01;作为 PHP 语言的又一次重要迭代&#xff0c;PHP 8.5 承诺带来一系列旨在提升代码可读性、健壮性以及开发者效率的改进。而更令人兴奋的是&#xff0c;借助强大的本地开发环境 ServBay&am…...

redis和redission的区别

Redis 和 Redisson 是两个密切相关但又本质不同的技术&#xff0c;它们扮演着完全不同的角色&#xff1a; Redis: 内存数据库/数据结构存储 本质&#xff1a; 它是一个开源的、高性能的、基于内存的 键值存储数据库。它也可以将数据持久化到磁盘。 核心功能&#xff1a; 提供丰…...