C++ 简单模拟实现 STL 中的 list 与 queue

目录

一,list

1, list 的节点与迭代器

2,list 的数据结构、一些简单的功能、构造函数

3,list 的对元素操作

4,C++ 11 的一些功能

5,完整代码:

二,queue


一,list

std::list 是 C++ 标准模板库 (STL) 中提供的一个容器模板,它被实现为环状双向链表。list 和 vector 是两个最常被使用的容器。相较于 vector 的连续线性空间,list 的非连续存储就显得复杂许多,它的好处是每次插人或删除一个元素,就配置或释放一个元素空间。因此,list 对空间的使用一点也不浪费。而且,对于任何位置的插入或移除元素,list 永远是常数时间。

关于 list 的各种接口的用法这里就不介绍了,此文主要是模拟实现。
 

1, list 的节点与迭代器

list 本身和 list 的节点是不同的结构,是需要分开设计的。list 中会包含 list 节点结构。节点结构:

template<class T>
struct list_node {list_node<T>* next, * pre;	// next 指向下一个节点,pre 指向前一个节点T data;list_node(const T& data_ = T()):next(nullptr),pre(nullptr),data(data_){}
};

如果不清楚迭代器是什么或者是不清楚要怎么设计一个容器专属的迭代器可以看看这个:C++ 迭代器与反向迭代器-CSDN博客

如果对 vector 的实现细节感兴趣可以看看这个:C++ 简单模拟实现 STL 中的 vector 与 stack-CSDN博客

list 不再能够像 vector 一样以普通指针作为迭代器,因为其节点不保证在储存空间中连续存在。所以 list 迭代器必须有能力指向 list 的节点,并且能够进行正确的递增(++)、递减(--)、取值(*)、成员存取(->)等操作。这里要注意,取值时取的是节点的数据值,不是节点本身,成员取用时取用的是节点的成员。例如:

mySTL::list<int> lt = { 1,2,3,4,5 };
for (auto it = lt.begin();it != lt.end();++it) {cout << *it << " ";    // 输出的是 1 2 3 4 5;
}
mySTL::list<pair<int, int>> lt2 = { {1,2},{3,4},{5,6} };
for (auto it = lt2.begin();it != lt2.end();++it) {cout << it->first << "," << it->second << " # ";	// 输出的是 1,2 # 3,4 # 5,6 #
}

list 迭代器的设计:

template<class T,class Ref,class Ptr>
struct list_iterator {typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> self;typedef list_iterator<T, T&, T*> iterator;// 迭代器内部的一个普通指针类型,指向对应的 list 节点Node* _node;// 迭代器相应类型的定义typedef T	value_type;typedef Ref reference;typedef Ptr pointer;// 迭代器的构造list_iterator(Node* node):_node(node){}					// 用节点构造当前迭代器list_iterator(const iterator& iter):_node(iter._node){}	// 用普通迭代器构造当前迭代器// 注意, 对迭代器解引用, 取出来的是节点里面的数据值reference operator*()const { return _node->data; }// &(operator*()), 这是一个标准的做法pointer operator->()const { return &(operator*()); }self& operator++() { _node = _node->next; return *this;}self operator++(int){Node* tmp = _node;_node = _node->next;return tmp;}self& operator--() { _node = _node->pre; return *this;}self operator--(int) {Node* tmp = _node;_node = _node->pre;return tmp;}bool operator==(const self& iter)const { return _node == iter._node; }bool operator!=(const self& iter)const { return not operator==(iter); }};

2,list 的数据结构、一些简单的功能、构造函数

list 是一个带有头节点的环状双向链表,它只需要一个指针就可以完整的表示整个链表,为了方便操作,我们也可以来额外来维护一个 size 变量来表示节点的个数(不包括头节点)。

template<class T>
class list {// ...
private:typedef list_node<T> Node;typedef list<T> self;private:Node* _head = nullptr;  //头节点size_t _size = 0;void initialize() {		//初始化头节点_head = new Node;_head->pre = _head->next = _head;}// ...
};

list 中有关迭代器的操作与一些简单的基础功能:

template<class T>
class list {// ...
public://迭代器/*正向迭代器*/typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;iterator begin() { return _head->next; }iterator end() { return _head; }const_iterator begin()const { return _head->next; }const_iterator end()const { return _head; }/*反向迭代器*/typedef Reverse_Iterator<iterator> reverse_iterator;typedef Reverse_Iterator<const_iterator> const_reverse_iterator;reverse_iterator rbegin() { return end(); }reverse_iterator rend() { return begin(); }const_reverse_iterator rbegin()const { return end(); }const_reverse_iterator rend()const { return begin(); }public:// 简单的基础功能size_t size() { return _size; }bool empty() { return _size == 0; }void clear() {while (not empty()) pop_back();}void swap(list& lt) {std::swap(_head, lt._head);std::swap(_size, lt._size);}// 对首尾元素的访问T& back() { return *(--end()); }const T& back()const { return *(--end()); }T& front() { return *begin(); }const T& front()const { return *begin(); }// ...
};

list 的构造函数:

template<class T>
class list {// ...
public://构造list() { initialize(); }list(size_t n, const T& data = T()) {initialize();while (n--) { push_back(data); }}list(int n, const T& data = T()) {initialize();while (n--) { push_back(data); }}/*使用迭代器构造*/template<class input_iterator>						list(input_iterator begin, input_iterator end) {initialize();while (begin != end) {push_back(*(begin++));}}//拷贝list(const list& lt) {initialize();for (const auto& data : lt) {push_back(data);}}self& operator=(list lt) {swap(lt);return *this;}//析构~list() {clear();delete _head;_head = nullptr;_size = 0;}// ...
};

3,list 的对元素操作

std::list 里面提供的元素操作有很多,这里就只挑几种重要的函数来实现。我们把 insert 与 erase 操作实现之后就可以很轻松的实现 push_back()、push_front()(尾插,头插) 与 pop_back() 、pop_front() (尾删,头删)操作了。

template<class T>
class list {// ...
public://增void push_front(const T& data) { insert(begin(), data); }	// 头插新节点void push_back(const T& data) { insert(end(), data); }		// 尾插新节点// 在 pos 位置的前面插入一个值为 data 的新节点, 返回新插入的节点的迭代器iterator insert(iterator pos, const T& data) {Node* node = pos._node;Node* newNode = new Node(data);newNode->next = node;newNode->pre = node->pre;node->pre->next = newNode;node->pre = newNode;++_size;return newNode;}//删void pop_front() { erase(begin()); }	// 头删void pop_back() { erase(--end()); }		// 尾删// 删掉迭代器 pos 所指向的节点, 返回删掉的节点的位置的新节点迭代器iterator erase(iterator pos) {assert(pos != end());Node* tmp = pos._node;tmp->pre->next = tmp->next;tmp->next->pre = tmp->pre;Node* res = tmp->next;delete tmp;--_size;return res;}// ...
};

4,C++ 11 的一些功能

这里主要实现的功能是 initializer_list 初始化右值引用,如果对这两个东西不了解的话可以看看这两篇博客:

C++11 一些常用的功能-CSDN博客

C++ 左值引用与右值引用-CSDN博客

template<class T>
class list {// ...
public://C++ 11 //initializer_list构造list(const std::initializer_list<T>& lt) {initialize();for (const T& data : lt) {push_back(data);}}//右值引用相关//移动构造与移动赋值list(list&& lt) :_head(lt._head), _size(lt._size) {initialize();lt._head = nullptr;lt._size = 0;}self& operator=(list&& lt) {delete* this;std::swap(_head, lt._head);std::swap(_size, lt._size);}//插入void push_front(T&& data) { insert(begin(), std::forward<T>(data)); }void push_back(T&& data) { insert(end(), std::forward<T>(data)); }iterator insert(iterator pos, T&& data) {Node* node = pos._node;Node* newNode = new Node(std::forward<T>(data));newNode->next = node;newNode->pre = node->pre;node->pre->next = newNode;node->pre = newNode;++_size;return newNode;}// ...
};

5,完整代码:

namespace mySTL {// list 节点类template<class T>struct list_node {list_node<T>* next, * pre;	// next 指向下一个节点,pre指向前一个节点T data;list_node(const T& data_ = T()):next(nullptr),pre(nullptr),data(data_){}};// list 迭代器类template<class T,class Ref,class Ptr>struct list_iterator {typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> self;typedef list_iterator<T, T&, T*> iterator;// 迭代器内部的一个普通指针类型,指向对应的 list 节点Node* _node;// 迭代器相应类型的定义typedef T	value_type;typedef Ref reference;typedef Ptr pointer;// 迭代器的构造list_iterator(Node* node):_node(node){}					// 用节点构造当前迭代器list_iterator(const iterator& iter):_node(iter._node){}	// 用普通迭代器构造当前迭代器// 注意, 对迭代器解引用, 取出来的是节点里面的数据值reference operator*()const { return _node->data; }// &(operator*()), 这是一个标准的做法pointer operator->()const { return &(operator*()); }self& operator++() { _node = _node->next; return *this;}self operator++(int){Node* tmp = _node;_node = _node->next;return tmp;}self& operator--() { _node = _node->pre; return *this;}self operator--(int) {Node* tmp = _node;_node = _node->pre;return tmp;}bool operator==(const self& iter)const { return _node == iter._node; }bool operator!=(const self& iter)const { return not operator==(iter); }};// list 类template<class T>class list {private:typedef list_node<T> Node;typedef list<T> self;private:Node* _head = nullptr;  //头节点size_t _size = 0;void initialize() {		//初始化头节点_head = new Node;_head->pre = _head->next = _head;}public://迭代器/*正向迭代器*/typedef list_iterator<T, T&, T*> iterator;					typedef list_iterator<T, const T&, const T*> const_iterator;iterator begin() { return _head->next; }iterator end() { return _head; }const_iterator begin()const { return _head->next; }const_iterator end()const { return _head; }/*反向迭代器*/typedef Reverse_Iterator<iterator> reverse_iterator;		typedef Reverse_Iterator<const_iterator> const_reverse_iterator;reverse_iterator rbegin() { return end(); }reverse_iterator rend() { return begin(); }const_reverse_iterator rbegin()const { return end(); }const_reverse_iterator rend()const { return begin(); }public:// 简单的基础功能size_t size() { return _size; }bool empty() { return _size == 0; }void clear() { while (not empty()) pop_back();}void swap(list& lt) {std::swap(_head, lt._head);std::swap(_size, lt._size);}// 对首尾元素的访问T& back() { return *(--end()); }const T& back()const { return *(--end()); }T& front() { return *begin(); }const T& front()const { return *begin(); }// 在迭代器 start 与 finish 构成的范围内查找值为 target 的节点iterator find(const iterator& start, const iterator& finish, const T& target) {for (iterator it = start;it != finish;++it) {if (*it == target) return it;}return finish;}public://构造list() { initialize(); }list(size_t n, const T& data = T()) {initialize();while (n--) { push_back(data); }}list(int n, const T& data = T()) {initialize();while (n--) { push_back(data); }}template<class input_iterator>						/*使用迭代器构造*/list(input_iterator begin, input_iterator end) {initialize();while (begin != end) {push_back(*(begin++));}}//拷贝list(const list& lt) {initialize();for (const auto& data : lt) {push_back(data);}}self& operator=(list lt) {swap(lt);return *this;}//析构~list() {clear();delete _head;_head = nullptr;_size = 0;}public://增void push_front(const T& data) { insert(begin(), data); }	// 头插新节点void push_back(const T& data) { insert(end(), data); }		// 尾插新节点// 在 pos 位置的前面插入一个值为 data 的新节点, 返回新插入的节点的迭代器iterator insert(iterator pos,const T& data) {Node* node = pos._node;Node* newNode = new Node(data);newNode->next = node;newNode->pre = node->pre;node->pre->next = newNode;node->pre = newNode;++_size;return newNode;}//删void pop_front() { erase(begin()); }	// 头删void pop_back() { erase(--end()); }		// 尾删// 删掉迭代器 pos 所指向的节点, 返回删掉的节点的位置的新节点迭代器iterator erase(iterator pos) {assert(pos != end());Node* tmp = pos._node;tmp->pre->next = tmp->next;tmp->next->pre = tmp->pre;Node* res = tmp->next;delete tmp;--_size;return res;}public://C++ 11 //initializer_list构造list(const std::initializer_list<T>& lt) {initialize();for (const T& data : lt) {push_back(data);}}//右值引用相关//移动构造与移动赋值list(list&& lt) :_head(lt._head), _size(lt._size) {initialize();lt._head = nullptr;lt._size = 0;}self& operator=(list&& lt) {delete *this;std::swap(_head, lt._head);std::swap(_size, lt._size);}//插入void push_front(T&& data) { insert(begin(), std::forward<T>(data)); }void push_back(T&& data) { insert(end(), std::forward<T>(data)); }iterator insert(iterator pos, T&& data) {Node* node = pos._node;Node* newNode = new Node(std::forward<T>(data));newNode->next = node;newNode->pre = node->pre;node->pre->next = newNode;node->pre = newNode;++_size;return newNode;}};}

二,queue

queue 是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口。queue 允许新增元素、移除元素、从最底端加人元素取得最顶端元素。但除了最底端可以加入、最顶端可以取出外,没有任何其它方法可以存取 queue 的其它元素。也就是说,queue 不允许有遍历行为。将元素推人queue 的操作称为 push,将元素推出 queue 的操作称为 pop。

queue 与 stack 一样,都是容器适配器(container adapters),他们的底层都是其他的容器,STL 中的 stack 与 queue 实际上都是对其他容器的封装,其都不支持迭代器。

我们可以选择用 list 也可以选择用 deque 来当作 queue 的底层容器。

namespace mySTL {template<class T, class Container = list<T>>class queue {private:Container _cont;	// 底层使用的容器public:void push(const T& data) { _cont.push_back(data); }void push(T&& data) { _cont.push_back(std::forward<T>(data)); }void pop() { _cont.pop_front(); }T& back() { return _cont.back(); }const T& back()const { return _cont.back(); }T& front() { return _cont.front(); }const T& front()const { return _cont.front(); }size_t size() { return _cont.size(); }bool empty() { return _cont.empty(); }};}

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

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

相关文章

开源 OLAP 及其在不同场景下的需求

目录 一、开源 OLAP 综述 二、OLAP场景思考 2.1 面向客户的报表 2.2 面向经营的报表 2.3 末端运营分析 2.4 用户画像 2.5 订单分析 2.6 OLAP技术需求思考 三、开源数据湖/流式数仓解决方案 3.1 离线数仓体系——Lambda架构 3.2 实时数据湖解决方案 3.3 实时分析解决…

Java毕业设计-基于springboot开发的校园台球厅人员与设备管理系统-毕业论文+答辩PPT(附源代码+演示视频)

文章目录 前言一、毕设成果演示&#xff08;源代码在文末&#xff09;二、毕设摘要展示1、开发说明2、需求分析3、系统功能结构 三、系统实现展示1、系统功能模块2、管理员功能模块3、用户功能模块 四、毕设内容和源代码获取总结 Java毕业设计-基于springboot开发的校园台球厅人…

Linux-1.常见指令以及权限理解

目录 本节目标 使用 XShell 远程登录 Linux 关于 Linux 桌面 下载安装 XShell 查看 Linux 主机 ip 使用 XShell 登陆主机 XShell 下的复制粘贴 Linux下基本指令 登录Linux服务器 新建多用户 全屏 1.快速认识5~6个命令 2.详细谈论课件的所有指令 01. ls 指令 02…

初识redis(一)

前言 引用的是这本书的原话 Redis[1]是一种基于键值对&#xff08;key-value&#xff09;的NoSQL数据库&#xff0c;与很多键值对数据库不同的是&#xff0c;Redis中的值可以是由string&#xff08;字符串&#xff09;、hash&#xff08;哈希&#xff09;、list&#xff08;列…

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查

33.HarmonyOS App(JAVA)鸿蒙系统app数据库增删改查 关系数据库 关系对象数据库&#xff08;ORM&#xff09; 应用偏好数据库 分布式数据库 关系型数据库&#xff08;Relational Database&#xff0c;RDB&#xff09;是一种基于关系模型来管理数据的数据库。HarmonyOS关系型…

Pandas与Jupyter Notebook的完美结合【第153篇—数据分析】

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 利用Python进行数据分析&#xff1a;Pandas与Jupyter Notebook的完美结合 在数据科学和分析…

职场口才提升之道

职场口才提升之道 在职场中&#xff0c;口才的重要性不言而喻。无论是与同事沟通协作&#xff0c;还是向上级汇报工作&#xff0c;亦或是与客户洽谈业务&#xff0c;都需要具备良好的口才能力。一个出色的职场人&#xff0c;除了拥有扎实的专业技能外&#xff0c;还应具备出色…

web自动化测试系列-selenium的安装和运行(一)

目录 web自动化系列之如何安装selenium 1.web自动化中的三大亮点技术 2.web自动化能解决什么问题 &#xff1f; 3.为什么是selenium ? 4.selenium特点 5.selenium安装 6.下载浏览器及驱动 7.测试代码 web自动化系列之如何安装selenium web自动化 &#xff0c;一个老生…

【C++】vector介绍

个人主页 &#xff1a; zxctscl 如有转载请先通知 文章目录 1. 前言2. vector的介绍3. Member functions3.1 (constructor)3.2 (destructor) 4. Capacity4.1 resize4.2 reserve4.3 shrink_to_fit 5. vector 增删查改5.1 push_back5.2 insert5.3 pop_back5.4 find5.5 erase 1. 前…

【C++】模板与泛型编程

文章目录 1. 泛型编程2. 函数模板2.1 函数模板概念2.2 函数模板格式2.3 函数模板的原理2.4 函数模板的实例化2.5 模板参数的匹配原则 3. 类模板3.1 类模板的定义格式3.2 类模板的实例化 4. 非类型模板参数5. 模板的特化5.1 概念5.2 函数模板特化5.3 全特化5.4 偏特化5.5 类模板…

Docker 搭建Redis集群

目录 1. 3主3从架构说明 2. 3主3从Redis集群配置 2.1关闭防火墙启动docker后台服务 2.2 新建6个docker容器实例 2.3 进去任意一台redis容器&#xff0c;为6台机器构建集群关系 2.4 进去6381&#xff0c;查看集群状态 3. 主从容错切换迁移 3.1 数据读写存储 3.1.1 查看…

【JDBC编程】基于MySql的Java应用程序中访问数据库与交互数据的技术

꒰˃͈꒵˂͈꒱ write in front ꒰˃͈꒵˂͈꒱ ʕ̯•͡˔•̯᷅ʔ大家好&#xff0c;我是xiaoxie.希望你看完之后,有不足之处请多多谅解&#xff0c;让我们一起共同进步૮₍❀ᴗ͈ . ᴗ͈ აxiaoxieʕ̯•͡˔•̯᷅ʔ—CSDN博客 本文由xiaoxieʕ̯•͡˔•̯᷅ʔ 原创 CSDN …

java switch用法

满足那个条件&#xff0c;就从那个入口进入&#xff0c;没有break就继续&#xff08;是这样设计的&#xff0c;需要自己加break;&#xff09;&#xff0c;一般都是要加break的。 switch (表达式) 表达式只能是【整型、char、String.】 import java.util.Scanner;public class…

微服务day07 -- ES集群

4.集群 单机的elasticsearch做数据存储&#xff0c;必然面临两个问题&#xff1a;海量数据存储问题、单点故障问题。 海量数据存储问题&#xff1a;将索引库从逻辑上拆分为N个分片&#xff08;shard&#xff09;&#xff0c;存储到多个节点 单点故障问题&#xff1a;将分片数…

Review(一)

&#x1f308;个人主页&#xff1a;Rookie Maker &#x1f525; 系列专栏&#xff1a;Rookie review &#x1f3c6;&#x1f3c6;关注博主&#xff0c;随时获取更多关于IT的优质内容&#xff01;&#x1f3c6;&#x1f3c6; &#x1f600;欢迎来到小田代码世界~ &#x1f601…

DRC检查及丝印的调整

DRC检查及丝印的调整 综述&#xff1a;本文主要讲述AD软件中DRC检查、丝印的调整以及logo的添加的相关步骤&#xff0c;附加logo添加的脚本链接和大量操作图片&#xff0c;使步骤详细直观。 1. 点击“工具”→“设计规则检查”→“运行DRC”。&#xff08;一开始可以只开启电…

一个程序从编译到运行的全过程

一个程序从编译到运行的全过程 一个程序从编译到运行的全过程编译预处理编译 汇编链接载入虚拟内存用户空间 总结 一个程序从编译到运行的全过程 每次用编译器写完一个程序后&#xff0c;我们会进行调试和执行&#xff0c;将代码的结果输出在我们的电脑屏幕上&#xff0c;但是…

Python爬虫学习完整版

一、什么是爬虫 网络爬虫&#xff0c;是一种按照一定规则&#xff0c;自动抓取互联网信息的程序或者脚本。由于互联网数据的多样性和资源的有限性&#xff0c;根据用户需求定向抓取相关网页并分析也成为如今主流的爬取策略。 1 爬虫可以做什么 你可以爬取网络上的的图片&#…

全民采矿石赚钱小程序源码,附带详细搭建教程

安装教程 1、环境用宝塔Nginxphp7.0或者以下版本 2、可以更换各种模板&#xff0c;懂代码和标签的可以改模板&#xff0c;不懂的可以直接上站 3、上站前记得添加关键词和内容库 4、伪静态在绑定完百度站长之后再添加 目录说明&#xff1a; data/keyword 放关键词 标签&#xff…

OpenLayers基础教程——WebGLPoints图层样式的设置方法

1、前言 前一篇博客介绍了如何在OpenLayers中使用WebGLPoints加载海量数据点的方法&#xff0c;这篇博客就来介绍一下WebGLPoints图层的样式设置问题。 2、样式运算符 在VectorLayer图层中&#xff0c;我们只需要创建一个ol.style.Style对象即可&#xff0c;WebGLPoints则不…