list模拟实现

list模拟实现

  • 结点类的模拟实现
    • 构造函数
  • 迭代器类的模拟实现
    • 迭代器类存在的意义
    • 迭代器类的模板参数
    • 构造函数
    • !=运算符重载
    • ==运算符重载
    • *运算符重载
    • ->运算符的重载
    • ++运算符的重载
    • - -运算符的重载
  • list模拟实现
    • 构造函数
    • 拷贝构造函数
    • 赋值运算符重载函数
    • 与迭代器相关的函数
      • begin与end
    • 访问容器相关的函数
      • front和back
    • 插入与删除函数
      • insert与erase
      • push_back与pop_back
      • front_back与front_back
    • 其他函数
      • clear
      • empty
      • swap

结点类的模拟实现

list的底层实现其实是一个链表,而且还是一个带头循环双向链表:
在这里插入图片描述
我们需要先实现一个结点类,结点类中包括数据域,前后结点的地址:

namespace gtt
{template<class T>struct list_node{T _data;list_node<T>* _prev;list_node<T>* _next;};
}

构造函数

结点类的构造函数就是根据所给数据来构造一个节点,节点的数据域存储数据,前后指针指向nullptr即可,若构造结点时未传入数据,则默认以list容器所存储类型的默认构造函数所构造出来的值为传入数据:

//构造函数
list_node(const T& val = T()):data(val),prev(nullptr),next(nullptr)
{}

迭代器类的模拟实现

迭代器类存在的意义

我们在学习vector与string过程中,并没有去创建一个迭代器类,而list就需要去创建一个迭代器类,这是为什么呢?

在vector与string学习中,我们可以发现,在物理内存中它俩都是一个连续的结果,此时的指针就相当于是一个原生指针,我们对指针进行自增,自减和解引用操作,就直接对相应位置是数据进行操作:
在这里插入图片描述
而对于我们对list来说,它是一个链表结构,在物理内存中并不是连续的,我们并不能只通过指针的自增,自减和解引用来对相应的数据进行操作:
在这里插入图片描述
既然list的指针不满足原生指针的要求,我们此时就可以对list的指针直接进行封装,通过运算符重载,来实现自增,自减,以及解引用等功能,让list可以和vector,string一样,看起来像原生指针一样对进行自增,自减和解引用就是在对数据进行操作。

我们通过这种封装的方式,可以使使用者不必关心容器底层实现,用起来的就是简单的方式访问数据。

迭代器类的模板参数

迭代器类模板参数一共有三个:

template<class T, class Ref, class Ptr>

我们为什么要传递三个参数呢?那是因为我们在实现list的时候,typedef了两个迭代器类型:

typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;

我们就可以发现,Ref和Ptr分别代表引用类型与指针类型,当我们调用const类型迭代器时,编译器就会实例化一个const类型迭代器出来,调用普通类型迭代器时,编译器就会实例化一个普通类型的迭代器。

构造函数

迭代器其实就是对指针进行了封装,他的成员变量就只有一个,就是节点指针,他的构造函数就是直接根据所给指针构造一个迭代器函数即可:

//构造函数
__list_iterator(Node* node):_node(node)
{}

!=运算符重载

使用!=运算符,本质就是看两个迭代器中的指针是否指向不同位置:

//不等于
bool operator!=(const iterator& it)const
{return _node != it._node;
}

==运算符重载

使用 ==运算符与使用 !=运算符意思相反,本质就是看两个迭代器中的指针是否指向相同位置:

//等于
bool operator==(const iterator& it)const
{return _node == it._node;
}

*运算符重载

解引用操作就是找到该节点的数据,我们直接返回当前节点指针所指向的数据即可,要注意的是这儿得使用引用返回,有可能需要对数据进行修改:

//解引用
Ref& operator*()
{return _node->_data;
}

->运算符的重载

->操作主要是针对于某些自定义类型时,比如日期类,我们需要访问它的成员变量时,单独打印出年月日,就需要用到->操作符了,我们直接返回当前指针所指向的数据即可:

//->
Ptr operator->()
{return &(operator*());//return &(_node->_data);
}

其实他本质上是应该是两箭头,以日期类为例:第一个箭头是pos ->去调用重载的operator->返回Date* 的指针,第二个箭头是Date* 的指针去访问对象当中的成员变量_year。

++运算符的重载

++运算符分为前置++和后置++,

  1. 前置++,string和vector中的前置++就是返回自增后的数据,我们为了让list也能表现出此特性,前置++就是返回后一个指针指向的结点:
//前置++
iterator& operator++()
{_node = _node->_next;return *this;
}
  1. 后置++,也就是先记录当前结点位置,进行自增以后,返回自增前指针指向的结点:
//后置++
iterator operator++(int)
{iterator tmp(*this);_node = _node->_next;return tmp;
}

- -运算符的重载

同样,- -运算符分为前置- -和后置- -,他的原理与前置++类似:

//前置--
iterator& operator--()
{_node = _node->_prev;//自减后指针指向下一个结点return *this;//返回自减后的结点
}
//后置--
iterator operator--(int)
{iterator tmp(*this);//记录当前结点位置_node = _node->_prev;//自减后指针指向下一个结点return tmp;//返回自减前结点
}

list模拟实现

构造函数

在这里插入图片描述
因为list是一个带头循环双向链表,我们只需要申请一个头结点,然后头尾都指向头结点即可:

template<class T>
class list
{typedef list_node<T> Node;
public:list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}
private:Node* _head;
};

拷贝构造函数

1.在这儿我们可以先构造一个容器出来,然后将在进行交换即可,这种就属于现代写法:

void emptyinit()
{_head = new Node;_head->_next = _head;_head->_prev = _head;
}
template <class InputIterator>
list(InputIterator first, InputIterator last)
{emptyinit();while (first != last){push_back(*first);first++;}
}
list(const list<T>& lt)
{emptyinit();//创建一个头结点list<T> tmp(lt.begin(), lt.end());//构造一个新容器swap(tmp);//容器进行交换
}

2.创建一个头结点,将原节点挨个尾插进去:

list(const list<T>& lt)
{emptyinit();for (const auto& e : lt){push_back(e);}
}

赋值运算符重载函数

1.传统写法:先调用clear()函数对容器进行清理,然后将lt节点挨个尾插到容器中

//传统写法
list<int>& operator=(const list<int>& lt)
{if (this != &lt)//判断是否自己给自己赋值{clear();//清理容器数据for (const auto& e : lt){push_back(e);//尾插}}return *this;
}

2.现代写法:函数传值传参,然后将容器进行交换

//现代写法
list<int>& operator=(list<int> lt)
{swap(lt);return *this;
}

与迭代器相关的函数

begin与end

begin函数是返回第一个有效数据的迭代器,end函数返回最后一个有效数据下一个位置的迭代器。

list中第一个有效数据的迭代器就是头结点的下一个结点构造出来的迭代器,最后一个有效数据下一个位置的迭代器就是头结点构造出来的迭代器:
在这里插入图片描述

//begin()
iterator begin()
{return iterator(_head->_next);
}
//end()
iterator end()
{return iterator(_head);
}

我们还需要重载一对const对象的begin()函数和end()函数:

//const begin()
const_iterator begin()const
{return const_iterator(_head->_next);
}
//const end()
const_iterator end()const
{return const_iterator(_head);
}

访问容器相关的函数

front和back

front和back就是获取第一个有限数据和最后一个有效数据,我们只需要返回第一个有效数据和最后一个有效数据即可:

//front()
T& front()
{return *begin();
}
//back()
T& back()
{return *(end()--);
}

我们还需要重载一对const对象front()和back()函数:

//const front()
const T& front()const
{return *begin();
}
//const back()
const T& back()const
{return *(end()--);
}

插入与删除函数

insert与erase

insert:所给迭代器之前插入一个新结点。

在这里插入图片描述

//insert
iterator insert(iterator pos, const T& x)
{Node* cur = pos._node;//记录pos位置指针Node* prev = cur->_prev;//记录pos前一个位置指针Node* newnode = new Node(x);//创建插入节点prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);
}

erase:删除所给迭代器位置的结点
在这里插入图片描述

//erase
iterator erase(iterator pos)
{assert(pos._node);//判断pos结点合法性Node* cur = pos._node;//记录pos结点指针Node* prev = cur->_prev;//记录pos前一个结点位置指针Node* next = cur->_next;//记录pos后一个结点位置指针prev->_next = next;next->_prev = prev;delete cur;//释放掉pos结点return iterator(next);
}

我们需要注意的是erase以后pos是会失效的,所以这儿返回的是erase后pos下一个位置的迭代器。

push_back与pop_back

push_back与pop_back就是尾插跟尾删,我们只需复用insert与erase函数即可:

//尾插
void push_back(const T& x)
{insert(end(), x);
}
//尾删
void pop_back()
{erase(end()--);
}

front_back与front_back

front_back与front_back就是头插跟头删,我们也只需复用insert与erase函数即可:

//头插
void push_front(const T& x)
{insert(begin(), x);
}
//头删
void front_back()
{erase(begin());
}

其他函数

clear

clear用于清空容器,我们只需遍历一遍,删除结点,最后只剩头结点即可:

//clear
void clear()
{auto it = lt.begin();while (it != end()){it = erase(it);}
}

empty

判断list是否为空,我们只需要判断begin()函数与end()函数迭代器的位置是否一样,此时就说明list中只有一个头结点。

//empty
bool empty()
{return (begin() == end());
}

swap

swap用于交换两个容器,我们只需将两个容器的头结点进行交换即可:

//swap
void swap(list<T>& lt)
{::swap(_head, lt._head);
}

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

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

相关文章

uniapp封装组件,选中后右上角显示对号√样式(通过css实现)

效果&#xff1a; 一、组件封装 1、在项目根目录下创建components文件夹&#xff0c;自定义组件名称&#xff0c;我定义的是xc-button 2、封装组件代码 <template><view class"handle-btn"><view :class"handleIdCode 1 ? select : unSelec…

2023.8.14论文阅读

文章目录 ESPNet: Efficient Spatial Pyramid of Dilated Convolutions for Semantic Segmentation摘要本文方法实验结果 DeepFusion: Lidar-Camera Deep Fusion for Multi-Modal 3D Object Detection摘要本文方法实验结果 ESPNet: Efficient Spatial Pyramid of Dilated Convo…

一、Kubernetes介绍与集群架构

Kubernetes介绍与集群架构 一、认识容器编排工具 docker machine 主要用于准备docker host现已弃用建议使用docker desktop docker compose Compose 是一个用于定义和运行多容器 Docker 应用程序的工具。使用 Compose&#xff0c;您可以使用 YAML 文件来配置应用程序的服务。…

STM32定时器TIM控制

一、CubeMX的设置 1、新建工程&#xff0c;进行基本配置 2、配置定时器TIM2 1&#xff09;定时器计算公式&#xff1a;&#xff08;以下两条公式相同&#xff09; Tout ((ARR1) * PSC1)) / Tclk TimeOut ((Prescaler 1) * (Period 1)) / TimeClockFren Tout TimeOut&…

一生一芯3——ubuntu下显示器扩展

刚进ubuntu时不知道如何完成屏幕扩展&#xff0c;查阅后发现是显卡驱动问题&#xff0c;这里需要调整内置显示器的驱动 打开附加驱动 选择显卡驱动如上&#xff08;其他没试过&#xff09; 应用更改 -> 下载后重启 重启完成后扩展显示器上就有显示了 在设置中调整显示屏顺…

电商3D产品渲染简明教程

3D 渲染让动作电影看起来更酷&#xff0c;让建筑设计变得栩栩如生&#xff0c;现在还可以帮助营销人员推广他们的产品。 从最新的《阿凡达》电影到 Spotify 的上一次营销活动&#xff0c;3D 的应用让一切变得更加美好。 在营销领域&#xff0c;3D 产品渲染可帮助品牌创建产品的…

2023-08-15 linux mipi 屏幕调试:有一个屏幕开机时候不显示,开机后按power 按键休眠唤醒就可以显示。原因是reset gpio 被复用

一、现象&#xff1a;今天更新了一个新版本的buildroot linux sdk &#xff0c;调试两个mipi 屏幕&#xff0c;这两个屏幕之前在其他的sdk都调好了的&#xff0c;所有直接把配置搬过来。但是有一个屏幕可以正常显示&#xff0c;有一个屏幕开机时候不显示&#xff0c;开机后按po…

银河麒麟安装php7.1.33

银河麒麟V10兼容CentOS 8 安装过程与CentOS类似。 TencentOS3.1安装PHPNginxredis测试系统_乐大师的博客-CSDN博客 可以参考之前我写的文章。 不过有2个细节不同&#xff0c;下面说下。 问题1&#xff1a;编译错误提示“error:off_t undefined” 解决方法&#xff1a; 编…

运维监控学习笔记7

Zabbix的安装&#xff1a; 1、基础环境准备&#xff1a; 安装zabbix的yum源&#xff0c;阿里的yum源提供了zabbix3.0。 rpm -ivh http://mirrors.aliyun.com/zabbix/zabbix/3.0/rhel/7/x86_64/zabbix-release-3.0-1.el7.noarch.rpm 这个文件就是生成了一个zabbix.repo 2、安…

华为开源自研AI框架昇思MindSpore应用案例:基于MindSpore框架的UNet-2D案例实现

目录 一、环境准备1.进入ModelArts官网2.使用CodeLab体验Notebook实例 二、环境准备与数据读取三、模型解析Transformer基本原理Attention模块 Transformer EncoderViT模型的输入整体构建ViT 四、模型训练与推理模型训练模型验证模型推理 近些年&#xff0c;随着基于自注意&…

UI设计师个人工作感悟5篇

UI设计师个人工作感悟一 工作一年了&#xff0c;结合我自身谈谈UI设计的重要性。现在主流的论坛建站程序有两种 Phpwind 和Discuz(Phpwind被阿里巴巴收购 Discuz被腾讯收购这两个论坛程序都是开源免费的)&#xff0c;利用这两种程序我都分别建立过论坛&#xff0c;我第一次用的…

C++入门篇7---string类

所谓的string类&#xff0c;其实就是我们所说的字符串&#xff0c;本质和c语言中的字符串数组一样&#xff0c;但是为了更符合C面向对象的特性&#xff0c;特地将它写成了一个单独的类&#xff0c;方便我们的使用 对其定义有兴趣的可以去看string类的文档介绍&#xff0c;这里…

Rabbitmq延迟消息

目录 一、延迟消息1.基于死信实现延迟消息1.1 消息的TTL&#xff08;Time To Live&#xff09;1.2 死信交换机 Dead Letter Exchanges1.3 代码实现 2.基于延迟插件实现延迟消息2.1 插件安装2.2 代码实现 3.基于延迟插件封装消息 一、延迟消息 延迟消息有两种实现方案&#xff…

微软杀入Web3:打造基于区块链的AI产品

作者&#xff1a;秦晋 2023年1月&#xff0c;微软向 ChatGPT 创建者 OpenAI 投资 100 亿美元&#xff0c;在AI业界引发格外关注。此举也让微软在AI的战略探索上提前取得有利位置。 2023年3月&#xff0c;微软软件工程师 Albacore 披露微软正在为Edge 浏览器测试内置的非托管加密…

全新 – Amazon EC2 M1 Mac 实例

去年&#xff0c;在 re: Invent 2021 大会期间&#xff0c;我写了一篇博客文章&#xff0c;宣布推出 EC2 M1 Mac 实例的预览版。我知道你们当中许多人请求访问预览版&#xff0c;我们尽了最大努力&#xff0c;却无法让所有人满意。不过&#xff0c;大家现在已经无需等待了。我很…

LeNet中文翻译

Gradient-Based Learning Applied to Document Recognition 基于梯度的学习应用于文档识别 摘要 使用反向传播算法训练的多层神经网络构成了成功的基于梯度的学习技术的最佳示例。给定适当的网络架构&#xff0c;基于梯度的学习算法可用于合成复杂的决策表面&#xff0c;该决策…

C++ STL priority_queue

目录 一.认识priority_queue 二. priority_queue的使用 三.仿函数 1.什么是仿函数 2.控制大小堆 3.TopK问题 四.模拟实现priority_queue 1.priority_queue的主要接口框架 2.堆的向上调整算法 3.堆的向下调整算法 4.仿函数控制大小堆 五.priority_queue模拟实现整体代码和测…

用js快速生成一个简单的css原子库 例如: .mr-18 .pl-18

第三方css原子库的缺点 比如 tailwindcss&#xff0c;有学习成本最开始写的时候效率可能还没有我们自己手写效率高&#xff0c;需要配置&#xff0c;会有原始样式被覆盖的问题&#xff1b;总之就是一个字重 自己搓的优点 学习成本低灵活不会有副作用 <!DOCTYPE html>…

Redis详解

Redis 简介 Redis&#xff08;Remote Dictionary Server&#xff09;是一个开源的高性能键值对存储数据库&#xff0c;最初由 Salvatore Sanfilippo 开发&#xff0c;它在内存中存储数据&#xff0c;并提供了持久化功能&#xff0c;可以将数据保存到磁盘中&#xff0c;是一种N…

Stephen Wolfram:那么…ChatGPT 在做什么,为什么它有效呢?

So … What Is ChatGPT Doing, and Why Does It Work? 那么…ChatGPT在做什么&#xff0c;为什么它有效呢&#xff1f; The basic concept of ChatGPT is at some level rather simple. Start from a huge sample of human-created text from the web, books, etc. Then train…