[C++] 模拟实现list(二)

标题:[C++] 模拟实现list(二)

@水墨不写bug



目录

(一)回顾

(二)迭代器类的封装设计

(1)成员函数简要分析

 (2)const迭代器类的设计

(3)list类的封装设计

(4)反向迭代器类的设计

(三)实现list类


正文开始:

(一)回顾

        本篇文章接续《[C++] 模拟实现list(二)》继续讲解STL实现list的逻辑和思考,最终提供实现的list的最终版本。


        [C++] 模拟实现list(二)中,我们定义了三个类:

template<typename T>
struct ListNode;template<typename T>
struct ListIterator;template<class T>
class list;

        ListNode即是list的一个节点;ListIterator即对节点的指针的封装的一个类,也就是迭代器;list即是链表的类;

         我们一般通过指针来维护链表,但是如果直接拿指针这个内置类型作为迭代器,迭代器的行为不符合我们的要求,所以我们将这个指针封装为一个类,这样就可以通过类的成员函数重载运算符来使得迭代器的行为符合我们的要求。


(二)迭代器类的封装设计

(1)成员函数简要分析

        我们实现迭代器类ListIterator,本质就是通过封装节点的指针,来实现要求的功能。这个迭代器类,看似是一个自定义类,其实我们可以把它的行为等效的看为一个指针类型:

        指针的前置++,就是节点指针先向后移动再使用。

        指针的前置--,就是节点指针的先向前移动,再使用。

        指针的后置++,后置--,先使用,再移动。

        指针的解引用,就得到了数据——然而对于封装的类,我们把它的行为定义为返回节点指针的数据。

        变量引用返回,得到这个变量本身——对于ListIterator类,我们返回数据T的引用;

        以及对==和 != 的重载,只需要比较节点指针内的数据是否一致即可。

 (2)const迭代器类的设计

        有普通的迭代器,就会有const迭代器,我们根据上面的分析,可以实现的普通迭代器如下:

template<typename T>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> Self;ListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_next;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_prev;return *this;}T& operator*(){return _node->_data;}T* operator->(){return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;
};

        如果第一次思考,你可能会想到直接在实例化的时候在普通迭代器前面加上const,但是这是错误的做法:如果直接在普通迭代器前面加上const,这就表明const修饰迭代器本身(类本身,也就是类对象本身不能修改:由于类内部只有一个节点的指针类型,所以这个指针本身不能修改,这是一个与事实相悖的结果:指针不能移动了!)

        想要解决上述问题,我们只有再定义一个类ConstListIterator,在类内部我们发现其实现与普通迭代器相比,只有两个不同的地方:

        这两个地方自然是涉及对象内数据的成员函数重载,他们是:

operator*()

operator->()

         如果我们执意要实现ConstListIterator类,可以实现如下:

template<typename T>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T> Self;ListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_next;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_prev;return *this;}const T& operator*(){return _node->_data;}const T* operator->(){return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;
};

通过比较两个类,我们发现只有他们的返回值不同而已,这两个类的大部分代码都是重复的。那么有没有一种方法可以缩减代码量,减少我们的工作量呢?

        我们可以通过额外多传入两个参数Ptr,Ref来解决这个问题:

template<typename T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_next;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_prev;return *this;}T& operator*(){return _node->_data;}Ref operator*() const{return _node->_data;}T* operator->(){return &(_node->_data);}Ptr operator->() const{return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;
};

        由于const迭代器与普通迭代器的大部分行为都是类似的,只有上述两个重载函数的返回值不同,这意味着const迭代器的*和->的返回值不能修改,是一个常量,这于事实相符。

        所以我们的第一个模板参数相同。都是T,而第二与第三个模板参数则根据迭代器类型的不同而不同:如果实例化的对象是const迭代器,则传入的Ref接受的是const T&,Ptr接受的是const T* ,我们这样就通过一个类模板就可以实现ListIterator和ConstListIterator两种迭代器类型。

(3)list类的封装设计

        由于我们在设计迭代器类的时候,把整个类设置为了struct,可以公开访问,这意味着我们可以在list类中使用迭代器ListIterator,这是我们现前就考虑的。但是在STL中,整个容器的设计按照迭代器模式设计,将迭代器类统称为iterator,所以与STL保持一致,同时与我们的迭代器类相互嵌合,所以我们先typedef出迭代器和const迭代器;接下来对于list的实现,就是我们的老面孔了——实现双向带头循环链表,你可以参考我的这篇文章:《实现双向链表》——里面有非常详细的讲解。

        所以,在这里我们不再赘述实现双向链表的过程。

(4)反向迭代器类的设计

        反向迭代器与普通迭代器的实现基本一致,只是在++改为节点前移动;--改为节点后移动。

反向迭代器类实现如下:

template<typename T, class Ref, class Ptr>
struct ReverseListIterator
{typedef ListNode<T> Node;typedef ReverseListIterator<T, Ref, Ptr> Self;ReverseListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_prev;return *this;}Self& operator--(){_node = _node->_next;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_prev;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_next;return *this;}T& operator*(){return _node->_data;}Ref operator*() const{return _node->_data;}T* operator->(){return &(_node->_data);}Ptr operator->() const{return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;
};

        需要注意的是,反向迭代器也有const类型,所以采用了和普通迭代器同样的处理方法:设置三个模板参数:<T,Ref,Ptr>

(三)实现list类

        源代码如下:

#pragma once
#include<iostream>
#include<cstdbool>
#include<cassert>
using namespace std;namespace ddsm
{template<typename T>struct ListNode{ListNode<T>* _prev;ListNode<T>* _next;T _data;ListNode(const T& val = T()):_prev(nullptr),_next(nullptr),_data(val){}};template<typename T,class Ref,class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;ListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_next;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_prev;return *this;}T& operator*(){return _node->_data;}Ref operator*() const{return _node->_data;}T* operator->(){return &(_node->_data);}Ptr operator->() const{return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;};template<typename T, class Ref, class Ptr>struct ReverseListIterator{typedef ListNode<T> Node;typedef ReverseListIterator<T, Ref, Ptr> Self;ReverseListIterator(Node* node = nullptr):_node(node){}Self& operator++(){_node = _node->_prev;return *this;}Self& operator--(){_node = _node->_next;return *this;}Self& operator++(int){Self tem(*this);_node = _node->_prev;return tem;}Self& operator--(int){Self tem(*this);_node = _node->_next;return *this;}T& operator*(){return _node->_data;}Ref operator*() const{return _node->_data;}T* operator->(){return &(_node->_data);}Ptr operator->() const{return &(_node->_data);}bool operator==(const Self& ltt){return _node == ltt._node;}bool operator!=(const Self& ltt){return !(*this == ltt);}Node* _node;};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;typedef ReverseListIterator<T, T&, T*> reverse_iterator;typedef ReverseListIterator<T, const T&, const T*> const_reverse_iterator;public://对于空链表的初始化(只创建一个头节点)void init_empty(){_pHead = new Node;_pHead->_next = _pHead;_pHead->_prev = _pHead;}//默认构造list(){init_empty();}// n 个值初始化list(int n, const T& value = T()){init_empty();for (size_t i = 0; i < n; i++){push_back(value);}}//拷贝构造,深拷贝//list<int> l1(l2)list(const list<T>& lt){init_empty();for (const auto& e : lt){push_back(e);}}//赋值重载现代写法//复用拷贝构造void operator=(list<T> lt){std::swap(_pHead,lt._pHead);}//初始化列表,初始化list(std::initializer_list<T> init){init_empty();for (const auto& e : init){push_back(e);}}void clear(){auto pos = begin();while (pos != end()){pos = erase(pos);//迭代器更新防止失效}}~list(){clear();delete _pHead;_pHead = nullptr;}/*void push_back(const T& value = T()) const{Node* newnode = new Node(value);newnode->_next = _pHead;newnode->_prev = _pHead->_prev;_pHead->_prev->_next = newnode;_pHead->_prev = newnode;}*//*void push_back(const T& value){Node* newnode = new Node(value);newnode->_next = _pHead;newnode->_prev = _pHead->_prev;_pHead->_prev->_next = newnode;_pHead->_prev = newnode;}*/reverse_iterator rbegin(){return reverse_iterator(_pHead->_prev);}const_reverse_iterator rbegin() const{return const_reverse_iterator(_pHead->_prev);}reverse_iterator rend(){return reverse_iterator(_pHead);}const_reverse_iterator rend() const{return const_reverse_iterator(_pHead);}iterator begin(){return iterator(_pHead->_next);}const_iterator begin() const{return const_iterator(_pHead->_next);}iterator end(){return iterator(_pHead);}const_iterator end() const{return const_iterator(_pHead);}//在指定位置之前插入iterator insert(iterator pos,const T& val = T()){Node* newnode = new Node(val);Node* cur = pos._node;Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}//删除指定位置的节点iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}void push_front(const T& value){insert(begin(), value);}void push_back(const T& value){insert(end(), value);}void pop_front(){erase(begin());}void pop_back(){erase(--end());}T& front(){return begin()._node->_data;}const T& front()const{return begin()._node->_data;}T& back(){return (--end())._node->_data;}const T& back()const{return (--end())._node->_data;}size_t size()const{int count = 0;for (const auto& e : *this){count++;}return count;}bool empty()const{return size() == 0;}private:Node* _pHead;};}

完~

未经作者同意禁止转载

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

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

相关文章

二四、3d人脸构建

一、下载github项目3dmm_cnn-master https://github.com/anhttran/3dmm_cnn.git 一个使用深度神经网络从单个图像进行 3D 人脸建模的项目,端到端代码,可直接根据图像强度进行 3D 形状和纹理估计;使用回归的 3D 面部模型,从检测到的面部特征点估计头部姿势和表情。…

19185 01背包问题

解决这个问题的关键是使用动态规划的方法。我们可以创建一个二维数组dp[i][j]&#xff0c;其中i表示考虑前i件物品&#xff0c;j表示背包的容量。dp[i][j]的值表示在考虑前i件物品&#xff0c;且背包容量为j时能获得的最大价值。 ### 算法步骤 1. 初始化一个二维数组dp&#x…

机器学习(五) -- 监督学习(7) --SVM2

系列文章目录及链接 上篇&#xff1a;机器学习&#xff08;五&#xff09; -- 监督学习&#xff08;7&#xff09; --SVM1 下篇&#xff1a; 前言 tips&#xff1a;标题前有“***”的内容为补充内容&#xff0c;是给好奇心重的宝宝看的&#xff0c;可自行跳过。文章内容被“文…

ABAQUS大连正版代理商:亿达四方——开启东北工业智能仿真新篇章

在东北老工业基地的振兴道路上&#xff0c;大连以其独特的地理位置和深厚的产业基础&#xff0c;成为推动区域经济发展的领头羊。作为国际知名的仿真软件ABAQUS在大连地区的官方授权代理商&#xff0c;亿达四方正以科技创新为驱动&#xff0c;引领当地制造业迈向数字化、智能化…

SD卡讲解

SD 卡 (Secure Digital Memory Card) 在我们生活中已经非常普遍了&#xff0c;控制器对 SD 卡进行读写通信 操作一般有两种通信接口可选&#xff0c;一种是 SPI 接口&#xff0c;另外一种就是 SDIO 接口。SDIO 全称是安全数 字输入/输出接口&#xff0c;多媒体卡 (MMC)、SD 卡、…

stm32使用pwm和编码器模式(包含重映射)

Dri_TIM.c #include "Dri_TIM.h"/*** description: TIM4初始化&#xff0c;pwm模式* return {*}*/ void Dri_TIM4_Init() {/* 1. 开启时钟*//* 1.1 定时器4的时钟 */RCC->APB1ENR | RCC_APB1ENR_TIM4EN;/* 1.2 GPIO的时钟 PB */RCC->APB2ENR | RCC_APB2ENR_I…

【Python实战因果推断】30_双重差分1

目录 Panel Data 在讨论了干预效果异质性之后&#xff0c;是时候转换一下思路&#xff0c;回到平均干预效果上来了。在接下来的几章中&#xff0c;您将学习如何利用面板数据进行因果推断。 面板数据是一种跨时间重复观测的数据结构。在多个时间段观察同一单位&#xff0c;可以…

PyTorch中的CPU和GPU代码实现详解

PyTorch中的CPU和GPU PyTorch中的CPU和GPU代码实现详解1. 安装PyTorch2. 编写支持CPU和GPU的PyTorch代码2.1 模型定义2.2 数据加载2.3 将模型和数据移动到GPU2.4 训练循环 3. 关键步骤详解**3.1 定义设备****3.2 模型和数据移动到GPU****3.3 优化器和损失函数** 4. 完整代码示例…

构建实时银行应用程序:英国金融机构 Nationwide 为何选择 MongoDB Atlas

Nationwide Building Society 超过135年的互助合作 Nationwide Building Society&#xff08;以下简称“Nationwide”&#xff09; 是一家英国金融服务提供商&#xff0c;拥有超过 1500 万名会员&#xff0c;是全球最大的建房互助会。 Nationwide 的故事可以追溯到 1884 年&am…

web后端开发--请求响应

目录 前言 请求 简单参数 原始方法 Spring方式 Post请求乱码处理 实体参数 简单实体参数 复杂实体参数 ​编辑 数组集合参数 数组参数 ​编辑 集合参数 日期参数 ​编辑 Json参数 ​编辑 传递json数据 json数组 json对象&#xff08;POJO&#xff09; jso…

Dify中的知识库API列表

1.知识库API列表 通过文本/文件创建/更新/删除文档/查询文档嵌入状态&#xff0c;知识库创建/知识库查询/文档列表查询&#xff0c;分段增/删/改/查。 接口名字功能描述请求示例POST/datasets/{dataset_id}/document/create_by_text通过文本创建文档此接口基于已存在知识库&a…

tableau人口金字塔,漏斗图,箱线图绘制 - 13

人口金字塔&#xff0c;漏斗图&#xff0c;箱线图 1. 金字塔1.1 定义1.2 金字塔创建1.2.1 数据导入1.2.2 数据异常排查1.2.3 创建度量字段1.2.4 转换属性1.2.5 创建数据桶1.2.6 选择相关属性1.2.7 年龄排序1.2.8 创建计算字段1.2.9 选择相关字段1.2.10 设置轴排序1.2.11 设置颜…

Windows系统服务器远程教程

在远程连接Windows系统服务器之前&#xff0c;需要确保以下几点&#xff1a; 被远程的Windows服务器必须开启远程桌面功能。这一功能在Windows系统中默认是关闭的&#xff0c;需要手动启用。 必须为两台计算机中的一台计算机&#xff08;即客户端&#xff09;创建远程桌面连接。…

11、中台-DDD-几种微服务架构模型对比分析

引言 在上一章中&#xff0c;我们深入探讨了DDD分层架构的基本概念和实现方法。这一章将重点介绍几种常用的微服务架构模型&#xff0c;包括洋葱架构、六边形架构&#xff0c;并对这两种架构模型与DDD分层架构进行对比分析。通过了解不同架构模型的优缺点&#xff0c;帮助我们…

C++复合数据类型:指针类型、引用类型、指针和引用之间的关系

复合数据类型 (1)指针 A.What&#xff08;什么是指针&#xff09; 用于存放对象地址的复合数据类型 B.Which&#xff08;有哪些指针&#xff09; 空指针&#xff1a; int *p nullptr; int *p 0;//&#xff08;不指向任何对象&#xff09;void *&#xff1a; void *&…

fastermaker-boot代码生成器

fastermaker-boot 是基于Spring Boot3 、Vue3 的一个代码简洁、结构清晰、开发高效、模块可扩展的单体项目的基础开发框架&#xff0c;包含代码生成器模块&#xff0c;适合初级开发者特别是大学生学习研究使用&#xff0c;也是中小型系统快速开发的利器。 开发技术: JDK 17、Sp…

liunx清理服务器内存和日志

1、查看服务器磁盘占用情况 # 查看磁盘占用大小 df -h 2、删除data文件夹下面的日志 3、查看每个服务下面的日志输出文件&#xff0c;过大就先停掉服务再删除out文件再重启服务 4、先进入想删除输入日志的服务文件夹下&#xff0c;查看服务进程&#xff0c;杀掉进程&#xff…

DW03D是一款用于锂离子/聚合物电池保护的高集成度解决方案。DW03D包含内部功率MOSFET、高精度电压检测电路和延迟电路

一般概述 DW03D产品是单节锂离子/锂聚合物可充电电池组保护的高集成度解决方案。DW03D包括了先进的功率MOSFET&#xff0c;高精度的电压检测电路和延时电路。 DW03D具有非常小的TSS08-8的封装,这使得该器件非常适合应用于空间限制得非常小的可充电电池组应用。…

【备战秋招】——算法题目训练和总结day3

【备战秋招】——算法题目训练和总结day3&#x1f60e; 前言&#x1f64c;BC149简写单词题解思路分析代码分享&#xff1a; dd爱框框题解思路分析代码分享&#xff1a; 除2&#xff01;题解思路分析代码分享&#xff1a; 总结撒花&#x1f49e; &#x1f60e;博客昵称&#xff…

Gradle 介绍

Gradle 定义 Gradle 是一个现代化的构建自动化工具&#xff0c;用于管理软件项目的构建过程和依赖关系。它通过一种灵活且强大的 DSL&#xff08;领域特定语言&#xff09;语法来描述项目的构建逻辑和任务&#xff0c;可以用于构建几乎任何类型的软件项目&#xff0c;从简单的应…