C++list模拟实现
C++list模拟实现
- list接口总结
- 结点类的模拟实现
- 迭代器的模拟实现
- 迭代器模板参数
- 迭代器类中的构造函数
- 迭代器类中的运算符重载
- operator++和operator - -
- operator!= 和operator==
- operator*
- operator->
- 总览
- list 类
- 构造函数
- 拷贝构造函数
- 赋值运算符重载operator=
- clear()
- 析构函数
- 迭代器相关函数
- begin和end
- 与容器修改有关函数
- insert 插入
- erase 删除
- push_back()尾插
- pop_back 尾删
- push_front 头插
- pop_front 头删
- size
list接口总结
namespace bite
{// List的节点类template<class T>struct ListNode{ListNode(const T& val = T());ListNode<T>* _pre;ListNode<T>* _next;T _date;};//List的迭代器类template<class T, class Ref, class Ptr>class Listiterator{typedef ListNode<T>* Node;typedef Listiterator<T, Ref, Ptr> Self;public:Listiterator(Node* Node);Ref operator*();Ptr operator->();Self& operator++();dSelf operator++(int);Self& operator--();Self& operator--(int);bool operator!=(const Self& l);bool operator==(const Self& l);private:Node _node;};//list类template<class T>class list{typedef ListNode<T> Node;public:typedef Listiterator<T, T&, T*> iterator;typedef Listiterator<T, const T&, const T&> const_iterator;public:///// List的构造void init();list();list(const list<T>& val)list<T>& operator==(list<T> val)~list();///// List Iteratoriterator begin();iterator end();const_iterator begin();const_iterator end();///// List Capacitysize_t size()const;bool empty()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>& lt);private:Node* _head;size_t _size;};
};
结点类的模拟实现
STL中的list为带头双向循环链表。
如图:

所以我们要实现list链表之前还要实现结点类。 结点中要存储前一个结点的地址和后一个结点的地址、还有数据。因此我们只需要设定成员变量和构造函数就行。
template <class T> //设定模板结点中的数据类型可以是不同的
struct ListNode
{typedef ListNode<T> Node;ListNode(const T& val = T())//构造函数:_prev(nullptr),_next(nullptr),_date(val){}Node* _prev;//前驱指针Node* _next;//后驱指针T _date;//数据};
迭代器的模拟实现
我们在string和vector中迭代器模拟都是直接给定模板指针。但是到了list中我们就需要实现一个迭代器类型。why?
这是因为我们在string和vector中数据的存储都是存放在一块连续的空间。我们可以同过指针进行对数据的增删查改。

list并不是存放一块连续的空间,而是通过结点存放下一个结点的地址一个一个连结起来的。 所以我们不能通过指针来实现链表的增删查改。
迭代器是一种用于遍历集合或容器中元素的接口,它可以隔离对容器的访问方式和底层实现,从而实现解耦。迭代器可以依次访问容器中的每一个元素,而无需了解容器的内部细节。
既然我们的指针不能满足迭代器的行为,那我们就将指针封装成类,使其能够满足迭代器的需要。
迭代器模板参数
迭代器我们给处三个模板参数。
template<class T, class Ref, class Ptr>
实现两个迭代器,一个iterator 和另一个 const_iterator。
typedef Listiterator<T, T&, T*> iterator;typedef Listiterator<T, const T&, const T&> const_iterator;
从这里我们可以看出,Ref 代表的是引用类型和指针类型。
迭代器类中的构造函数
我们迭代器的底层就是对指针进行的封装,因此成员变量就一个就是结点指针。
Listiterator(Node* node):_node(node)
{}
迭代器类中的运算符重载
operator++和operator - -
list的迭代器没必要实现operator+,只需要实现自增和和自减
自增:
//前置++
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;
}
operator!= 和operator==
实现两个迭代器之间的比较。
bool operator!=(const self& val)
{return _node != val._node;
}bool operator==(const self& val)
{return _node == val._node;
}
operator*
当我们使用解引用操作符,想要得到的是这个地址所指向的数据。所以我们operator* 返回该地址所指向的数据就行。
Ref operator*()
{return _node->_date;
}
operator->
我们在使用list时,如果list内结点存储的数据不是内置类型,而是自定义类型,例如Date日期类。我们访问Date类中的成员,就需要用到operator->。
例如:
struct A //自定义类型
{A(int x=0 ,int y =0):_aa1(x),_aa2(y){}int _aa1;int _aa2;
};
void test()
{A aa1(0,0);list<A> lt;lt.push_back(aa1);lt.push_back(A(1, 1));lt.push_back({ 2,2 });lt.push_back({3,3});list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_aa1 << " " << it->_aa2 << endl;;it++;}
}
operator->只需要返回结点数据的地址就行。
Ptr operator->()
{return &_node->_date;
}
这里有人就可能发现,这种方式访问自定义类型成员需要用到两个->。


这里其实是忽略了一个->,两个->第一个运算符重载的调用是指向A* ,另一个是原生指针的调用。为了让代码有良好的可读性,编译器做了特殊处理,忽略了一个->。
总览
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){}Ref operator*(){return _node->_date;}Ptr operator->(){return &_node->_date;}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;}bool operator!=(const self& val){return _node != val._node;}bool operator==(const self& val){return _node == val._node;}};
list 类
构造函数
list是一个双向带头循环链表,list初始化需要设置一个哨兵位,并使其的前驱指针和后驱指针都指向自己。
void init()
{_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;
}
list()
{init();
}
拷贝构造函数
list的拷贝构造就是根据所给的对象,拷贝处一个新的对象。拷贝构造函数我们先给一个头结点使其前驱指针和后驱指针都指向自己,然后再遍历对象,让对象中的结点一个一个尾插入到新的list中去。
list(const list<T>& val)
{init();for (auto& e : val){push_back(e);}
}
赋值运算符重载operator=
我们这里直接使用现代写法。利用传入的参数通过编译器自动调用list构造函数构造出一个list对象,
再使用swap使其与原来的容器进行交换。
list<T>& operator==(list<T> val){swap(val);return *this;}
clear()
clear函数只需要将链表中的结点和数据清理即可,给一个迭代器使其指向头结点,然后遍历链表将结点一个一个删除,除了哨兵位以外。
void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}
析构函数
析构函数首先我们先将list容器中的数据清除,让后在释放哨兵位和让_size归零。
~list()
{clear();delete _head;_head = nullptr;_size = 0;
}
迭代器相关函数
begin和end
在这里我们需要清楚的是begin为哨兵位的下一个结点,而end就是哨兵位。
iterator begin()
{return _head->_next;
}iterator end()
{return _head;
}
还有const begin 和end
const_iterator begin() const
{return _head->_next;
}const_iterator end() const
{return _head;
}
与容器修改有关函数
insert 插入
insert函数作用是在pos位置插入一个新的结点。
如图:

我们要在pos位置插入一个新的结点(newnode)就得让新结点前驱指针指向pos结点前一个结点(prev),让prev的后驱指针指向newnode。newnode后驱指针指向pos结点,pos前驱指针指向newnode。
void insert(iterator pos , const T& val)
{Node* newnode = new Node(val);Node* cur = pos._node;Node* prev = cur->_prev;newnode->_next = cur;cur->_prev = newnode;newnode->_prev = prev;prev->_next = newnode;_size++;}
erase 删除
erase就是删除pos位置的结点。
如图:

iterator erase(iterator pos)
{assert(pos != _head);Node* del = pos._node;Node* prev = del->_prev;Node* next = del->_next;prev->_next = next;next->_prev = prev;delete del;--_size;return iterator(next);
}
注意: erase还要注意迭代器失效的问题因此我们的返回值指向的是下一个结点。
push_back()尾插
双向带头循环链表的尾插比较容易理解。新建一个结点使其的前驱指针指向尾结点,尾结点的后驱结点指向新的结点。新结点的后驱指针指向哨兵位,哨兵位的前驱指针指向新结点,再++_size。

void push_back(const T& val)
{Node* newnode = new Node(val);Node* tail = _head->_prev;newnode->_prev = tail;tail->_next = newnode;newnode->_next = _head;_head->_prev = newnode;_size++;
}
还可以复用insert函数,是push_back函数变得简单。
void push_back(const T& val) { insert(end(), val); }
pop_back 尾删
我们这里直接复用erase函数。
void pop_back(){erase(--end()); }
push_front 头插
头插也是复用insert函数。
void push_front(const T& val)
{ insert(begin(), val);
}
pop_front 头删
void pop_front(){ erase(begin()); }
size
返回结点个数。
size_t size()
{return _size;
}
相关文章:
C++list模拟实现
Clist模拟实现 list接口总结结点类的模拟实现迭代器的模拟实现迭代器模板参数迭代器类中的构造函数迭代器类中的运算符重载operator和operator - -operator! 和operatoroperator*operator->总览 list 类构造函数拷贝构造函数赋值运算符重载operatorclear…...
设计模式(22):解释器模式
解释器 是一种不常用的设计模式用于描述如何构成一个简单的语言解释器,主要用于使用面向对象语言开发的解释器和解释器设计当我们需要开发一种新的语言时,可以考虑使用解释器模式尽量不要使用解释器模式,后期维护会有很大麻烦。在项目中&…...
kubernetes docker版本安装测试
文章目录 测试环境kubernetes安装环境配置安装程序下载镜像初始化reset环境init构建kubernetes配置授权信息配置网络插件查看状态 简单实例测试 测试环境 [rootlocalhost ~]# cat /etc/redhat-release CentOS Linux release 7.9.2009 (Core)kubernetes安装 参考kuberneter文档…...
策略模式:灵活调整算法的设计精髓
在软件开发中,策略模式是一种行为型设计模式,它允许在运行时选择算法的行为。通过定义一系列算法,并将每个算法封装起来,策略模式使得算法可以互换使用,这使得算法可以独立于使用它们的客户。本文将详细介绍策略模式的…...
[INS-30014]无法检查指定的位置是否位于 CFS 上
文章目录 一、具体错误二、通用解决方案1、可能的问题原因2、解决方案3、常见原因之hosts文件配置问题hosts配置方法hosts文件不可编辑解决办法 一、具体错误 在安装ORACLE19c的时候,出现无法检查指定的位置是否位于CFS上 二、通用解决方案 1、可能的问题原因 遇…...
机器学习和深度学习 -- 李宏毅(笔记与个人理解)Day 13
Day13 Error surface is rugged…… Tips for training :Adaptive Learning Rate critical point is not the difficult Root mean Square --used in Adagrad 这里为啥是前面的g的和而不是直接只除以当前呢? 这种方法的目的是防止学习率在训练过程中快速衰减。如果只用当前的…...
[Python图像识别] 五十二.水书图像识别 (2)基于机器学习的濒危水书古文字识别研究
该系列文章是讲解Python OpenCV图像处理知识,前期主要讲解图像入门、OpenCV基础用法,中期讲解图像处理的各种算法,包括图像锐化算子、图像增强技术、图像分割等,后期结合深度学习研究图像识别、图像分类应用。目前我进入第二阶段Python图像识别,该部分主要以目标检测、图像…...
Jmeter针对多种响应断言的判断
有时候response返回的结果并非一种,有多种,需要对这几种进行判断的时候需要使用Bean Shell。 (1)首先获取响应数据 String response prev.getResponseDataAsString(); ResponseCode 响应状态码 responseHeaders 响应头信息 res…...
Harmony鸿蒙南向驱动开发-Regulator接口使用
功能简介 Regulator模块用于控制系统中某些设备的电压/电流供应。在嵌入式系统(尤其是手机)中,控制耗电量很重要,直接影响到电池的续航时间。所以,如果系统中某一个模块暂时不需要使用,就可以通过Regulato…...
【opencv】示例-grabcut.cpp 使用OpenCV库的GrabCut算法进行图像分割
left mouse button - set rectangle SHIFTleft mouse button - set GC_FGD pixels CTRLleft mouse button - set GC_BGD pixels 这段代码是一个使用OpenCV库的GrabCut算法进行图像分割的C程序。它允许用户通过交互式方式选择图像中的一个区域,并利用GrabCut算法尝试…...
GEE数据集——巴基斯坦国家级土壤侵蚀数据集(2005 年和 2015 年)
简介 巴基斯坦国家级土壤侵蚀数据集(2005 年和 2015 年) 该数据集采用修订的通用土壤流失方程 (RUSLE),并考虑了六个关键影响因素:降雨侵蚀率 (R)、土壤可侵蚀性 (K)、坡长 (L)、坡陡 (S)、覆盖管理 (C) 和保护措施 (P)ÿ…...
服务器代理
服务器代理 配置:64G内存1 3090(24g)1P4000(8g) SSH连接 工作路径:/home/ubuntu/workspace/python Anaconda路径:/home/Ubuntu 1.在工作路径下创建自己的文件夹作为workspace 2.以用户ubunbtu登…...
【SGDR】《SGDR:Stochastic Gradient Descent with Warm Restarts》
arXiv-2016 code: https://github.com/loshchil/SGDR/blob/master/SGDR_WRNs.py 文章目录 1 Background and Motivation2 Related Work3 Advantages / Contributions4 Method5 Experiments5.1 Datasets and Metric5.2 Single-Model Results5.3 Ensemble Results5.4 Experiment…...
如何将arping以及所有依赖打包安装到另外一台离线ubuntu机器
ubuntu系统下可以使用arping命令检测局域网内一些ip是否冲突,使用方式为: arping xx.xx.xx.xx 在线情况下,可以使用下面命令下载arping,然后使用即可 apt install arping 但是有些情况下机器可能不能上网,这时就需要将…...
mac上如何安装python3
mac上如何安装python3? 安装homebrew 在终端执行命令 /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)" 执行完成后,homebrew和pip等工具就自动安装好了。 接下来安装python3.在终端…...
Java 那些诗一般的 数据类型 (下篇)
本篇会加入个人的所谓鱼式疯言 ❤️❤️❤️鱼式疯言:❤️❤️❤️此疯言非彼疯言 而是理解过并总结出来通俗易懂的大白话, 小编会尽可能的在每个概念后插入鱼式疯言,帮助大家理解的. 🤭🤭🤭可能说的不是那么严谨.但小编初心是能让更多人能接…...
WEB3.0:互联网的下一阶段
随着互联网的发展,WEB3.0时代正在逐步到来。本文将深入探讨WEB3.0的定义、特点、技术应用以及未来展望,为读者带来全新的思考。 一、什么是WEB3.0? WEB3.0可以被理解为互联网发展的下一阶段,是当前WEB2.0的升级版。相较于2.0时代…...
Fastgpt配合chatglm+m3e或ollama+m3e搭建个人知识库
概述: 人工智能大语言模型是近年来人工智能领域的一项重要技术,它的出现标志着自然语言处理领域的重大突破。这些模型利用深度学习和大规模数据训练,能够理解和生成人类语言,为各种应用场景提供了强大的文本处理能力。AI大语言模…...
如何使用选择器精确地控制网页中每一个元素的样式?
1. 基础知识 什么是 CSS 元素选择器 CSS 元素选择器是一种在网页中通过元素类型来应用样式的方法。 简单来说,它就像是一个指挥棒,告诉浏览器哪些 HTML 元素需要应用我们定义的 CSS 样式规则。 为何要使用 CSS 元素选择器 使用元素选择器可以让我们…...
各个微前端框架的优劣浅谈
各个微前端框架都有其独特的优势和劣势,下面我将针对几个主流的微前端框架进行简要的优劣分析: single-spa 优势: 轻量级:single-spa是一个非常轻量级的微前端框架,它主要提供了一个加载和管理微应用的机制,…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
Flask RESTful 示例
目录 1. 环境准备2. 安装依赖3. 修改main.py4. 运行应用5. API使用示例获取所有任务获取单个任务创建新任务更新任务删除任务 中文乱码问题: 下面创建一个简单的Flask RESTful API示例。首先,我们需要创建环境,安装必要的依赖,然后…...
基于距离变化能量开销动态调整的WSN低功耗拓扑控制开销算法matlab仿真
目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.算法仿真参数 5.算法理论概述 6.参考文献 7.完整程序 1.程序功能描述 通过动态调整节点通信的能量开销,平衡网络负载,延长WSN生命周期。具体通过建立基于距离的能量消耗模型&am…...
抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
对WWDC 2025 Keynote 内容的预测
借助我们以往对苹果公司发展路径的深入研究经验,以及大语言模型的分析能力,我们系统梳理了多年来苹果 WWDC 主题演讲的规律。在 WWDC 2025 即将揭幕之际,我们让 ChatGPT 对今年的 Keynote 内容进行了一个初步预测,聊作存档。等到明…...
BCS 2025|百度副总裁陈洋:智能体在安全领域的应用实践
6月5日,2025全球数字经济大会数字安全主论坛暨北京网络安全大会在国家会议中心隆重开幕。百度副总裁陈洋受邀出席,并作《智能体在安全领域的应用实践》主题演讲,分享了在智能体在安全领域的突破性实践。他指出,百度通过将安全能力…...
短视频矩阵系统文案创作功能开发实践,定制化开发
在短视频行业迅猛发展的当下,企业和个人创作者为了扩大影响力、提升传播效果,纷纷采用短视频矩阵运营策略,同时管理多个平台、多个账号的内容发布。然而,频繁的文案创作需求让运营者疲于应对,如何高效产出高质量文案成…...
Web后端基础(基础知识)
BS架构:Browser/Server,浏览器/服务器架构模式。客户端只需要浏览器,应用程序的逻辑和数据都存储在服务端。 优点:维护方便缺点:体验一般 CS架构:Client/Server,客户端/服务器架构模式。需要单独…...
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析
Java求职者面试指南:Spring、Spring Boot、Spring MVC与MyBatis技术解析 一、第一轮基础概念问题 1. Spring框架的核心容器是什么?它的作用是什么? Spring框架的核心容器是IoC(控制反转)容器。它的主要作用是管理对…...
Python 高效图像帧提取与视频编码:实战指南
Python 高效图像帧提取与视频编码:实战指南 在音视频处理领域,图像帧提取与视频编码是基础但极具挑战性的任务。Python 结合强大的第三方库(如 OpenCV、FFmpeg、PyAV),可以高效处理视频流,实现快速帧提取、压缩编码等关键功能。本文将深入介绍如何优化这些流程,提高处理…...
