[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 卡、…

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

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

构建实时银行应用程序:英国金融机构 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 设置颜…

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…

【Java数据结构】初识线性表之一:顺序表

使用Java简单实现一个顺序表 顺序表是用一段物理地址连续的存储单元依次存储数据元素的线性结构&#xff0c;一般情况下采用数组存储。在数组上完成数据的增删查改。 线性表大致包含如下的一些方法&#xff1a; public class MyArrayList { private int[] array; pri…

怎么将mkv视频格式转为mp4?这四种转换方法你肯定要试试!

怎么将mkv视频格式转为mp4&#xff1f;你是否曾被MKV格式的魅力深深吸引&#xff0c;仿佛踏入了一个充满奇幻色彩的多媒体秘境&#xff0c;那里&#xff0c;音频如溪流潺潺&#xff0c;视频似画卷铺展&#xff0c;字幕则如同夜空中最亮的星&#xff0c;三者交织成一场视听盛宴&…

【彻底禁用Windows系统的自动更新,让电脑使用更顺心!】

文章底部关注公众号&#xff1a;电脑维修小马 回复关键词即可获取软件及注册表&#xff1a;禁用更新 功能简介 自动更新是Windows系统的一项重要功能&#xff0c;旨在保持操作系统的安全性和最新状态。然而&#xff0c;对于许多用户来说&#xff0c;自动更新并不总是那么受欢迎…

渔人杯——RE

贪吃蛇的秘密 修改代码后&#xff0c;报了一个错 # uncompyle6 version 3.9.1 # Python bytecode version base 3.7.0 (3394) # Decompiled from: Python 3.11.8 (tags/v3.11.8:db85d51, Feb 6 2024, 22:03:32) [MSC v.1937 64 bit (AMD64)] # Embedded file name: snake1.py…

2023 N1CTF-n1canary

文章目录 参考n1canary模板类和模板函数make_unique和unique_ptrstd::unique_ptr示例&#xff1a; std::make_unique示例&#xff1a; 结合使用示例 operator->getrandom逆向源码思路exp 参考 https://nese.team/posts/n1ctf2023/ n1canary 模板类和模板函数 template &…

TCP协议的三次握手和四次挥手(面试)

三次握手 首先可以简单的回答&#xff1a; 1、第一次握手&#xff1a;客户端给服务器发送一个 SYN 报文。 2、第二次握手&#xff1a;服务器收到 SYN 报文之后&#xff0c;会应答一个 SYNACK 报文。 3、第三次握手&#xff1a;客户端收到 SYNACK 报文之后&#xf…

内存巨头SK海力士正深化与TSMC/NVIDIA合作关系,开发下一代HBM

据BusinessKorea报道&#xff0c;内存巨头SK海力士正深化与台积电(TSMC)及英伟达(NVIDIA)的合作关系&#xff0c;并计划在9月的台湾半导体展(Semicon Taiwan)上宣布更紧密的伙伴关系。 SK海力士与台积电的合作历史已久。2022年&#xff0c;台积电在其北美技术研讨会上宣布成立O…