C++初阶:适合新手的手撕list(模拟实现list)
上次讲了常用的接口:今天就来进行模拟实现啦
文章目录
- 1.基本结构与文件规划
- 2.空参构造函数(constructor)
- 3.完善迭代器(iterator)(begin(),end())
- 4.List Capacity(size(),empty())
- 4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)
- 6.clear()和swap()
- 7. 完善构造函数
- 7.1list (size_type n, const value_type& val = value_type());
- 7.2利用迭代器进行构造
- 7.3拷贝构造
- 8.重载=
- 9.析构函数
- 10.反向迭代器
1.基本结构与文件规划

- list.h头文件:包含类的全部(函数的声明与定义)
- reverseIterator.h文件:进行反向迭代器的实现
- test.cpp源文件:进行调用test函数,测试和完善功能
基本结构:
#pragma oncenamespace MyList
{// List的节点类template<class T>struct ListNode{ListNode<T>* _prev;ListNode<T>* _next;T _data;ListNode(const T& x=T()):_prev(nullptr),_next(nullptr),_data(x){}};//List的迭代器类template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node)//构造函数:_node(node){}ListIterator(const Self& l)//拷贝构造函数:_node(l._node){}T& operator*();T* operator->();Self& operator++();Self operator++(int);Self& operator--();Self& operator--(int);bool operator!=(const Self& l);bool operator==(const Self& l);};//list类template<class T>class list{typedef ListNode<T> Node;//默认是private 不给外面用public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;//构造函数list();list(int n, const T& value = T());template <class Iterator>list(Iterator first, Iterator last);//析构~list();// List Iteratoriterator begin();iterator end();const_iterator begin();const_iterator end();// List Capacitysize_t size()const;bool empty()const;// List AccessT& front();const T& front()const;T& back();const T& back()const;// 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);// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos);void clear();void swap(list<T>& l);private:Node* _head;};
};
ListNode结构体:- 定义了链表的节点结构,包含了三个成员变量:前驱指针
_prev、后继指针_next和数据_data。 - 构造函数初始化了这些成员变量,允许在创建节点时指定初始值。
- 定义了链表的节点结构,包含了三个成员变量:前驱指针
ListIterator结构体:- 定义了链表的迭代器结构,包含了指向节点的指针
_node。 - 重载了一系列操作符,如
*、->、++、--、!=、==,以便于对链表进行遍历和操作。
- 定义了链表的迭代器结构,包含了指向节点的指针
list类:- 包含了迭代器的定义、构造函数、析构函数以及一系列的操作函数。
- 定义了两种迭代器类型:
iterator和const_iterator,分别用于可修改的迭代和只读的迭代。 - 实现了一系列的操作函数
2.空参构造函数(constructor)
list(){_head = new Node;//去调用Node的默认构造函数了_head->_next = _head;_head->_prev = _head;//带头双向循环链表是这样的}
使用new:动态开辟+调用默认构造函数了
3.完善迭代器(iterator)(begin(),end())
这里为什么我们要把迭代器封装为一个类呢?明明之前模拟vector和string时,就直接typedef了
之前模拟
vector和string时,二者底层都是连续的,想要到下一个可以直接++;想要得到里面的数据可以直接*。但是现在对于
list是不行的,我们就需要重载各种运算符,但是底层又是一个指针(内置类型)不能重载,现在就只能封装进一个类里,就能重载了
//List的迭代器类template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;ListIterator(Node* node)//构造函数:_node(node){}ListIterator(const Self& l)//拷贝构造函数:_node(l._node)//这里是浅拷贝(写不写都行)//新创建的迭代器和原迭代器指向相同的内存地址{}Ref operator*(){return _node->_data;}Ptr operator->(){return &(_node->_data);}Self& operator++()//前置{_node = _node->_next;//自己要更新return *this;}Self operator++(int){Self s(*this);_node = _node->_next;return s;}Self& operator--(){_node = _node->_prev;//自己要更新return *this;}Self& operator--(int){Self s(*this);_node = _node->_prev;return s;}bool operator!=(const Self& l){return _node != l._node;}bool operator==(const Self& l){return _node == l._node;}};//list类template<class T>class list{typedef ListNode<T> Node;//默认是private 不给外面用public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T&> const_iterator;//构造函数list(){_head = new Node;//去调用Node的默认构造函数了_head->_next = _head;_head->_prev = _head;//带头双向循环链表是这样的}// List Iteratoriterator begin(){return _head->_next;//隐式类型转换(由单参构造函数支撑)}iterator end(){return _head;}const_iterator begin(){return _head->_next;}const_iterator end(){return _head;}private:Node* _head;};
};
4.List Capacity(size(),empty())
// List Capacitysize_t size()const{size_t size = 0;iterator it = begin();while (it != end()){size++;++it;}return size;}bool empty()const{return size() == 0;}
4.增删改查(push_back,pop_back,pop_front,push_front,insert,erase)
// 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){Node* cur = pos._node;Node* prev = pos._node->_prev;Node* newnode = new Node(val);//创建新节点newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;prev->_next = cur;return newnode;//隐式类型转换}// 删除pos位置的节点,返回该节点的下一个位置iterator erase(iterator pos){assert(pos != _head);Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return next;}
使用test1函数看功能是否正常
void print(MyList::list<int>& lt){list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it; // 更新迭代器}cout << endl;}void test1(){MyList::list<int> lt;lt.push_back(1);lt.push_back(2);//尾插2个print(lt);lt.pop_back();//尾删一个print(lt);lt.push_front(0);//头插一个print(lt);lt.pop_front();//头删一个print(lt);}

6.clear()和swap()
void clear(){//删除除头结点(_head)以外的节点iterator it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& l){std::swap(_head, l._head);}
7. 完善构造函数
7.1list (size_type n, const value_type& val = value_type());
list(int n, const T& value = T())
{_head = new Node;_head->_next = _head;_head->_prev = _head;for (int i = 0; i < n; ++i){push_back(value);}
}

7.2利用迭代器进行构造
template <class Iterator>list(Iterator first, Iterator last){while (first != last){push_back(*first);first++;}}
为什么使用模版:
因为可能使用其他类型的迭代器来进行初始化
7.3拷贝构造
list(const list<T>& lt){_head = new Node;_head->_next = _head;_head->_prev = _head;iterator it = copy.begin();while (it != copy.end()){push_back(*it);it++;}}
8.重载=
list<T>& lt operator=(list<T> lt){swap(lt);return *this;}
注意这里的参数不是常量引用,而是按值传递的。这是因为在赋值操作符中我们会调用
swap函数,按值传递可以保证传入的参数会被复制一份,避免对原对象的修改。在函数体内,我们调用了swap函数,将当前对象和传入的对象进行内容交换,然后返回*this,即当前对象的引用。
9.析构函数
~list(){clear();delete _head;_head = nullptr;}
调用clear函数后,就只剩下头结点了
10.反向迭代器
我们再次使用封装的思想,封装一个反向迭代器进去
#pragma oncetemplate <class iterator,class Ref,class Pre>
struct reserve_iterator
{typedef reserve_iterator<iterator, Ref, Pre> self;iterator _it;reserve_iterator(iterator it):_it(it) {}self& operator++(){--_it;return *this;}self operator++(int){self tmp = *this;--_it;return tmp;}self& operator--(){++_it;return *this;}self operator--(int){self tmp = *this;++_it;return tmp;}Ref operator*(){iterator tmp = _it;--tmp;return *tmp;}Pre operator->(){return &(operator*());}bool operator!=(const self& s){return _it != s._it;}bool operator==(const self& s){return _it == s._it;}};
此时那list类里就是这样:

好啦,list的内容也结束啦,下次就是Stack和Queue了。感谢大家支持!!!
相关文章:
C++初阶:适合新手的手撕list(模拟实现list)
上次讲了常用的接口:今天就来进行模拟实现啦 文章目录 1.基本结构与文件规划2.空参构造函数(constructor)3.完善迭代器(iterator)(begin(),end())4.List Capacity(size(),empty())4.增删改查(push_back,pop_back,pop_f…...
js手写Promise(上)
目录 构造函数resolve与reject状态改变状态改变后就无法再次改变 代码优化回调函数中抛出错误 thenonFulfilled和onRejected的调用时机异步then多个then 如果是不知道或者对Promise不熟悉的铁铁可以先看我这篇文章 Promise 构造函数 在最开始,我们先不去考虑Promi…...
基于Web技术的家居室内温湿度监测系统
设计一个基于Web技术的家居室内温湿度监测系统涉及前端和后端开发,以及与硬件传感器的集成。以下是一个简单的设计概述: ### 1. 系统架构 - **前端**: 用户界面,用于显示实时数据和历史记录,可通过Web浏览器访问。 - **后端**: 服…...
ubuntu22.04@laptop OpenCV Get Started: 009_image_thresholding
ubuntu22.04laptop OpenCV Get Started: 009_image_thresholding 1. 源由2. image_thresholding应用Demo2.1 C应用Demo2.2 Python应用Demo 3. 重点分析3.1 Binary Thresholding ( THRESH_BINARY )3.2 Inverse-Binary Thresholding ( THRESH_BINARY_INV )3.3 Truncate Threshold…...
Zeek实战—快速构建流量安全能力
第1章 网络流量与网络安全 1.2流量与网络 从宏观角度进行观察,如果将计算机网络看作一个整体,可以很容易抽象出它是由以下3个部分组成的。 1.网络终端。指连接在网络中的、能够产生或消费网络流量的软/硬件系统,是网络流量在正常情况下的…...
vim命令编辑完文件后,按ESC键退出编辑模式,无法进入命令模式解决方案
发现问题 在Vim编辑器中,我们通常需要按Esc键来退出编辑模式并进入命令模式。但有时,你可能会发现即使按了Esc键,也无法进入命令模式。这可能是由于某些设置或插件导致的。不过,有一个解决办法可以帮助你解决这个问题。 解决办法…...
【生产实测有效】Linux磁盘清理常用命令
经常遇到磁盘空间告警需要清理 常用方法 磁盘空间分析 先查看整体磁盘空间使用情况 df -Th lsblk 再有针对性的查看使用率过高的磁盘 du -hsx --exclude/{proc,sys,dev,boot,home,tmp,usr,var,app,ncltybbpo} /*查找大文件 find . -type d -exec tar -cjvf {}.tar.bz2 {…...
练习:鼠标类设计之1_类内容解析
前言 光做理论上的总结,不做练习理解不会那么深刻 做类的练习,解析类里面的内容有哪些 引入 电脑使用最频繁的两个外设:鼠标和键盘,他们每时每刻都在和用户交互,试做一个鼠标类 思路 我们现在要做一个鼠标类,这个类是属于能动类还是资源类呢?鼠标似乎自己做不了什么,需要和其…...
消息队列RabbitMQ-使用过程中面临的问题与解决思路
消息队列在使用过程中会出现很多问题 首先就是消息的可靠性,也就是消息从发送到消费者接收,消息在这中间过程中可能会丢失 生产者到交换机的过程、交换机到队列的过程、消息队列中、消费者接收消息的过程中,这些过程中消息都可能会丢失。 …...
搜索Agent方案
为啥需要整体方案,直接调用搜索接口取Top1返回不成嘛?要是果真如此Simple&Naive,New Bing岂不是很容易复刻->.-> 我们先来看个例子,前一阵火爆全网的常温超导技术,如果想回答LK99哪些板块会涨,你…...
排序算法---计数排序
原创不易,转载请注明出处。欢迎点赞收藏~ 计数排序(Counting Sort)是一种线性时间复杂度的排序算法,其核心思想是通过统计待排序元素的个数来确定元素的相对位置,从而实现排序。 具体的计数排序算法步骤如下ÿ…...
STM32——LCD(1)认识
目录 一、初识LCD 1. LCD介绍 2. 显示器的分类 3. 像素 4. LED和OLED显示器 5. 显示器的基本参数 (1)像素 (2)分辨率 (3)色彩深度 (4)显示器尺寸 (5ÿ…...
iTop-4412 裸机程序(二十二)- RTC时钟
目录 0.源码1. RTC2. iTop4412 中的 RTC使用的相关寄存器3. BCD编码4. 关键源码 0.源码 GitHub:https://github.com/Kilento/4412NoOS 1. RTC RTC是实时时钟(Real Time Clock)的缩写,是一种用于计算机系统的硬件设备࿰…...
Kafka 之 AdminClient API
目录 一. 前言 二. KafkaAdminClient API 2.1. API 总览 2.2. Topic 操作 2.2.1. 创建 Topic 2.2.2. Topic 列表 2.2.3. 删除 Topic 2.2.4. 描述 Topic 详细信息 2.3. 分区 Partition 操作 2.3.1. 增加分区 2.3.2. 分区副本重新分配 2.3.3. 查询分区副本列表 2.4.…...
Flutter run 一直 Running Gradle task ‘assembleDebug’…
发生缘由 Flutter 项目引入 fluttertoast 插件后,执行 Flutter run 一直 Running Gradle task ‘assembleDebug’…,最后发现下载 kotlin-compiler-embeddable-7.1.0.jar 特别的缓慢。 运行环境 电脑系统版本:Windows 10 64bit VS Code&…...
kali无线渗透之用wps加密模式破解出wpa模式的密码12
WPS(Wi-Fi Protected Setup,Wi-Fi保护设置)是由Wi-Fi联盟推出的全新Wi-Fi安全防护设定标准。该标准推出的主要原因是为了解决长久以来无线网络加密认证设定的步骤过于繁杂之弊病,使用者往往会因为步骤太过麻烦,以致干脆不做任何加密安全设定&…...
【Python】高级数据类型
🚩 WRITE IN FRONT 🚩 🔎 介绍:"謓泽"正在路上朝着"攻城狮"方向"前进四" 🔎🏅 荣誉:2021|2022年度博客之星物联网与嵌入式开发TOP5|TOP4、2021|2222年获评…...
挑战杯 python区块链实现 - proof of work工作量证明共识算法
文章目录 0 前言1 区块链基础1.1 比特币内部结构1.2 实现的区块链数据结构1.3 注意点1.4 区块链的核心-工作量证明算法1.4.1 拜占庭将军问题1.4.2 解决办法1.4.3 代码实现 2 快速实现一个区块链2.1 什么是区块链2.2 一个完整的快包含什么2.3 什么是挖矿2.4 工作量证明算法&…...
如何给最小化安装的CentOS主机装个远程桌面?
正文共:888 字 18 图,预估阅读时间:1 分钟 前面我们领微软云Azure的免费主机时(白嫖党618福利!来Azure领200美刀!外加云主机免费用一年!),发现“有资格免费试用服务”的主…...
知识图谱:py2neo将csv文件导入neo4j
文章目录 安装py2neo创建节点-连线关系图导入csv文件删除重复节点并连接边 安装py2neo 安装python中的neo4j操作库:pip install py2neo 安装py2neo后我们可以使用其中的函数对neo4j进行操作。 图数据库Neo4j中最重要的就是结点和边(关系)&a…...
AI-调查研究-01-正念冥想有用吗?对健康的影响及科学指南
点一下关注吧!!!非常感谢!!持续更新!!! 🚀 AI篇持续更新中!(长期更新) 目前2025年06月05日更新到: AI炼丹日志-28 - Aud…...
【第二十一章 SDIO接口(SDIO)】
第二十一章 SDIO接口 目录 第二十一章 SDIO接口(SDIO) 1 SDIO 主要功能 2 SDIO 总线拓扑 3 SDIO 功能描述 3.1 SDIO 适配器 3.2 SDIOAHB 接口 4 卡功能描述 4.1 卡识别模式 4.2 卡复位 4.3 操作电压范围确认 4.4 卡识别过程 4.5 写数据块 4.6 读数据块 4.7 数据流…...
CentOS下的分布式内存计算Spark环境部署
一、Spark 核心架构与应用场景 1.1 分布式计算引擎的核心优势 Spark 是基于内存的分布式计算框架,相比 MapReduce 具有以下核心优势: 内存计算:数据可常驻内存,迭代计算性能提升 10-100 倍(文档段落:3-79…...
JVM垃圾回收机制全解析
Java虚拟机(JVM)中的垃圾收集器(Garbage Collector,简称GC)是用于自动管理内存的机制。它负责识别和清除不再被程序使用的对象,从而释放内存空间,避免内存泄漏和内存溢出等问题。垃圾收集器在Ja…...
视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
dify打造数据可视化图表
一、概述 在日常工作和学习中,我们经常需要和数据打交道。无论是分析报告、项目展示,还是简单的数据洞察,一个清晰直观的图表,往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server,由蚂蚁集团 AntV 团队…...
Unity | AmplifyShaderEditor插件基础(第七集:平面波动shader)
目录 一、👋🏻前言 二、😈sinx波动的基本原理 三、😈波动起来 1.sinx节点介绍 2.vertexPosition 3.集成Vector3 a.节点Append b.连起来 4.波动起来 a.波动的原理 b.时间节点 c.sinx的处理 四、🌊波动优化…...
为什么要创建 Vue 实例
核心原因:Vue 需要一个「控制中心」来驱动整个应用 你可以把 Vue 实例想象成你应用的**「大脑」或「引擎」。它负责协调模板、数据、逻辑和行为,将它们变成一个活的、可交互的应用**。没有这个实例,你的代码只是一堆静态的 HTML、JavaScript 变量和函数,无法「活」起来。 …...
【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...
阿里云Ubuntu 22.04 64位搭建Flask流程(亲测)
cd /home 进入home盘 安装虚拟环境: 1、安装virtualenv pip install virtualenv 2.创建新的虚拟环境: virtualenv myenv 3、激活虚拟环境(激活环境可以在当前环境下安装包) source myenv/bin/activate 此时,终端…...
