C++ ——STL容器【list】模拟实现

在这里插入图片描述

代码仓库:

list模拟实现

list源码

数据结构——双向链表

文章目录

  • 🍇1. 节点结构体
  • 🍈2. list成员
  • 🍉3. 迭代器模板
  • 🍊4. 迭代器
  • 🍋5. 插入删除操作
    • 🍌5.1 insert & erase
    • 🍌5.2 push_back & push_front & pop_back & pop_front
  • 🍍6. 构造 & 析构 & 拷贝构造
  • 🥭7. 赋值重载
  • 🍓8. 获取元素个数

🍇1. 节点结构体

源码的list是双向带头循环链表,所以我们定义两个节点,一个指向下一个,一个指向前一个

template<class T>
struct list_node
{list_node<T>* _next;	//指向下一个节点list_node<T>* _prev;	//指向前一个T _val;	//数据list_node(const T& val = T()):_next(nullptr), _prev(nullptr), _val(val){}
};

🍈2. list成员

list类包含一个_head头节点,然后为了方便查出当前有多少个节点,还能多定义一个_size

template<class T>
class list
{typedef list_node<T> Node;
public:// 各类操作方法//...
private:Node* _head;size_t _size;
}

🍉3. 迭代器模板

image-20230730105307091

源码的迭代器设置了三个模板参数:

  • T:表示指向list节点的数据类型
  • Ref:迭代器的引用类型,通常情况为T&,但也可表示const
  • Ptr:表示指向节点的指针类型,通常情况下为T*,但也可表示const迭代器,避免代码的冗余

对于string或者是vector的迭代器,对其解引用就可以表示当前的数据;而list是链表,解引用之后表示的一个节点,所以相对会麻烦一点

//Ref T& / const T&
//Ptr T* / const T*
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_iterator<T, Ref, Ptr> self;typedef list_node<T> Node;Node* _node;__list_iterator(Node* node):_node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}//前置++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& lt){return _node != lt._node;}bool operator==(const self& lt){return _node == lt._node;}};

Tips:

迭代器并没有写拷贝构造,那么就是默认浅拷贝。这无关影响,因为我们就是希望通过这个迭代器找到这个节点

void test1()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);//浅拷贝list<int>::iterator it = lt.begin();while(it!=lt.end()){cout<< *it << " ";++it;}cout<<endl;
}

这里没有奔溃也是因为迭代器没有写析构函数,迭代器只是负责访问,并不负责管理

🍊4. 迭代器

const const_iterator begin() const
{//单参数构造函数 隐式类型转换return _head->_next;
}
const const_iterator end() const
{return _head;
}iterator begin()
{//单参数构造函数 隐式类型转换return _head->_next;
}
iterator end()
{return _head;
}

🍋5. 插入删除操作

🍌5.1 insert & erase

这里插入删除操作之后,也会存在当前迭代器失效,所以传修改完毕之后的迭代器位置

iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;Node* tmp = new Node(x);Node* prev = cur->_prev;prev->_next = tmp;tmp->_next = cur;cur->_prev = tmp;tmp->_prev = prev;++_size;return tmp;
}
iterator erase(iterator pos)
{assert(pos != end());Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;
}

🍌5.2 push_back & push_front & pop_back & pop_front

写了指定位置插入删除之后,直接复用即可

void push_back(const T& x)
{insert(end(), x);
}void push_front(const T& x)
{insert(begin(), x);
}void pop_back()
{Node* tail = _head->_prev;erase(tail);
}
void pop_front()
{erase(begin());
}

🍍6. 构造 & 析构 & 拷贝构造

查看源码发现list的构造和析构都采用了复用

image-20230730115520781
清空链表

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}//_size = 0;
}

复用

void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}list()
{empty_init();
}list(const list<T>& lt)
{empty_init();for (auto& e : lt){push_back(e);}
}
~list()
{clear();delete _head;_head = nullptr;
}

🥭7. 赋值重载

这里还是采用现代的写法,交换完毕之后,自动调用析构函数

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

🍓8. 获取元素个数

size_t size()
{return _size;
}

以上就是list的基本功能实现,实质上就是双向带头循环链表,迭代器这块有点复杂。

那本期分享就到这里咯,我们下期再见,如果还有下期的话。

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

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

相关文章

15 文本编辑器vim

15.1 建立文件命令 如果file.txt就是修改这个文件&#xff0c;如果不存在就是新建一个文件。 vim file.txt 使用vim建完文件后&#xff0c;会自动进入文件中。 15.2 切换模式 底部要是显示插入&#xff0c;是编辑模式&#xff1b; 按esc&#xff0c;底部要是空白的&#xff0…

微服务契约测试框架Pact-Python实战

Pact是一个契约测试框架&#xff0c;有多种语言实现&#xff0c;本文以基于pact-python探究契约测试到底是什么&#xff1f;以及如何实现 官网&#xff1a;自述文件 |契约文档 (pact.io) 契约测试步骤 1、为消费者写一个单元测试&#xff0c;让它通过&#xff0c;并生成契约…

OS-08-事件驱动:C10M是如何实现的?

08-事件驱动&#xff1a;C10M是如何实现的&#xff1f; 你好&#xff0c;我是陶辉。 上一讲介绍了广播与组播这种一对多通讯方式&#xff0c;从这一讲开始&#xff0c;我们回到主流的一对一通讯方式。 早些年我们谈到高并发&#xff0c;总是会提到C10K&#xff0c;这是指服务…

Kubernetes(K8s)从入门到精通系列之七:K8s的基本概念和术语之安全类

Kubernetes K8s从入门到精通系列之七:K8s的基本概念和术语之安全类 一、安全类二、Role和ClusterRole三、RoleBinding和ClusterRoleBinding一、安全类 开发的Pod应用需要通过API Server查询、创建及管理其他相关资源对象,所以这类用户才是K8s的关键用户。K8s设计了Service A…

MIT 6.830数据库系统 -- lab five

MIT 6.830数据库系统 -- lab five 项目拉取引言搜索练习1 BTreeFile.findLeafPage() 插入练习2 Spliting Page 删除练习3 页再分配练习4 合并页 事务小结 项目拉取 原项目使用ant进行项目构建&#xff0c;我已经更改为Maven构建&#xff0c;大家直接拉取我改好后的项目即可: …

centos安装mysql8

安装过程 基本安装过程同mysq5.7&#xff0c;参考&#xff1a;centos7安装mysql5.7_centos7 安装mysql5.7源_shuair的博客-CSDN博客 设置不区分大小写 切记&#xff0c;在第一次启动器前就应修改配置文件&#xff0c;设置不区分大小写&#xff0c;即初始化之前修改&#xff…

使用 docker 一键部署 MongoDB

目录 1. 前期准备 2. 导入镜像 3. 部署MongoDB脚本 4. 配置模板文件 5. 部署MongoDB 6. 部署后配置 7. 基本维护 1. 前期准备 新部署前可以从仓库&#xff08;repository&#xff09;下载 MongoDB 镜像&#xff0c;或者从已有部署中的镜像生成文件&#xff1a; # 查看…

Zookeeper入门介绍

Zookeeper在我本次系统的学习之前是已经开始使用了&#xff0c;但是并不理解Zookeeper到底是什么&#xff0c;有什么作用&#xff0c;你或许跟我有一样的疑惑&#xff0c;本专栏将会解决这些疑惑。 目录 Zookeeper介绍&#xff1a; zookeeper特点&#xff1a; 数据结构&#x…

NUMA架构在kubernetes中的应用

numactl使用 numactl 通过将 CPU 划分多个 node 减少 CPU 对总线资源的竞争,一般使用在高配置服务器部署多个 CPU 消耗性服务使用。 numactl使用,numa常用命令,numa命令行使用 #numactl -H available: 2 nodes (0-1) node 0 cpus: 0 2 4 6 8 10 12 14 16 18 20 22 node 0…

09mysql之数据处理之增删改

#1. 创建数据库dbtest11 CREATE DATABASE IF NOT EXISTS dbtest11 CHARACTER SET utf8; #2. 运行以下脚本创建表my_employees USE dbtest11; CREATE TABLE my_employees( id INT(10), first_name VARCHAR(10), last_name VARCHAR(10), userid VARCHAR(10), salary DOUBLE(10,2)…

《MySQL 实战 45 讲》课程学习笔记(二)

日志系统&#xff1a;一条 SQL 更新语句是如何执行的&#xff1f; 与查询流程不一样的是&#xff0c;更新流程还涉及两个重要的日志模块&#xff1a;redo log&#xff08;重做日志&#xff09;和 binlog&#xff08;归档日志&#xff09;。 重要的日志模块&#xff1a;redo l…

【VSCode部署模型】导出TensorFlow2.X训练好的模型信息

参考tensorflow2.0 C加载python训练保存的pb模型 经过模型训练及保存&#xff0c;我们得到“OptimalModelDataSet2”文件夹&#xff0c;模型的保存方法(.h5或.pb文件)&#xff0c;参考【Visual Studio Code】c/c部署tensorflow训练的模型 其中“OptimalModelDataSet2”文件夹保…

Doris安装部署入门

文章目录 一 Doris 介绍1.1 使用场景1.1.2 Doris架构 二 Doris单机部署2.1 下载 Doris2.2 配置 Doris2.2.1 配置 FE2.2.2 启动 FE2.2.3 查看 FE 运行状态2.2.4 连接 FE2.2.5 停止 FE 节点2.2.6 配置 BE2.2.7 启动 BE2.2.8 添加 BE 节点到集群2.2.9 查看 BE 运行状态2.2.10 停止…

GitHub仓库如何使用

核心&#xff1a;GitHub仓库如何使用 目录 1.创建仓库&#xff1a; 2.克隆仓库到本地&#xff1a; 3.添加、提交和推送更改&#xff1a; 4.分支管理&#xff1a; 5.拉取请求&#xff08;Pull Requests&#xff09;&#xff1a; 6.合并代码&#xff1a; 7.其他功能&…

网络知识整理

网络知识整理 网络拓扑网关默认网关 数据传输拓扑结构层面协议层面 网络拓扑 网关 连接两个不同的网络的设备都可以叫网关设备&#xff0c;网关的作用就是实现两个网络之间进行通讯与控制。 网关设备可以是交换机(三层及以上才能跨网络) 、路由器、启用了路由协议的服务器、代…

k8s Webhook 使用java springboot实现webhook 学习总结

k8s Webhook 使用java springboot实现webhook 学习总结 大纲 基础概念准入控制器&#xff08;Admission Controllers&#xff09;ValidatingWebhookConfiguration 与 MutatingWebhookConfiguration准入检查&#xff08;AdmissionReview&#xff09;使用Springboot实现k8s-Web…

Shell脚本学习2

文章目录 Shell脚本学习2运算符算术运算符关系运算符布尔运算符字符串运算符文件测试运算符 字符串拼接字符串获取字符串长度截取字符串查找字符串 数组条件控制if语句case语句for循环while循环until循环跳出循环 Shell脚本学习2 运算符 Bash 支持很多运算符&#xff0c;包括…

Linux 学习记录57(ARM篇)

Linux 学习记录57(ARM篇) 本文目录 Linux 学习记录57(ARM篇)一、外部中断1. 概念2. 流程图框 二、相关寄存器1. GIC CPU Interface (GICC)2. GIC distributor (GICD)3. EXTI registers 三、EXTI 寄存器1. 概述2. 内部框图3. 寄存器功能描述4. EXTI选择框图5. EXTI_EXTICR1 &…

vue3 +element动态表单实现

可以直接复制&#xff0c;接口看后端 父页面 <schedulesref"schedulesRef":dxbz"props.dxbz":jdlx"props.jdlx":woId"myWoId":addendumList"formInline.addendumList"v-if"addendumShow"addendum"addendu…

如何快速同步第三方平台数据?

全量的数据主要是针对多个系统的历史数据,大概有几千万数据,只需要初始化一次即可。 而增量的数据,是系统后续变更的数据。 这个需求其实不简单,至少有以下难点: 不能直接访问第三方数据库。 不能将历史数据导出到excel中,有泄露数据的风险。 如何快速同步历史数据? 增…