【STL模版库】模拟实现list类模版 {深度剖析list迭代器,实现list深拷贝}

news2025/1/10 3:07:42

一、核心结构

template <class T>
struct list_node{ //[1]
  T _data; //[2]
  list_node *_next; //指向下一个节点
  list_node *_prev; //指向前一个节点
  
  list_node(const T &val = T())
    :_data(val),
    _next(nullptr),
    _prev(nullptr)
  {}
};

template <class T>
class Mylist{
  typedef list_node<T> Node; //[3]
  Node *_phead; //[4] 
  //......
};

解释

  • [1] 使用struct公开成员变量,方便Mylist类中直接访问节点数据。
  • [2] 节点中存储的数据是模版类型,使链表可以存储各种类型包括自定义类型的数据。
  • [3] typedef为类型起别名,简化代码,便于后期维护。
  • [4] Mylist类只有一个成员变量,即指向哨兵位节点的指针(Mylist是一个带头双向循环链表)。

结构图示:
在这里插入图片描述


二、迭代器

2.1 结构及构造

//list_iterator
template <class T, class Ref, class Ptr>
struct list_iterator{ //[1]
  typedef list_node<T> Node;
  typedef list_iterator<T, Ref, Ptr> iterator;
  Node *_pnode; //[2]

  list_iterator(Node *pnode) //[3]
    :_pnode(pnode)
  {}

解释:

  • [1] 使用struct公开成员变量,方便Mylist类中直接访问迭代器指针数据。
  • [2] list_iterator的成员变量只有一个指向节点的指针。但其中封装了list迭代器的方法
    1. 解引用返回数据_data的引用
    2. 箭头返回数据_data的地址
    3. ++操作实现了链表指针的在链表上的移动,等等
  • [3] 用一个指向节点的指针构造list迭代器。
  • [4] list_iterator的拷贝构造,赋值重载,析构使用默认生成的即可(内部只有一个指针,且不涉及资源申请)。
//begin() & end()
template <class T>
class Mylist{
  //......
  public:
  //iterator
  typedef list_iterator<T, T&, T*> iterator;
  typedef list_iterator<T, const T&, const T*> const_iterator; //[2]

  iterator begin(){
    return iterator(_phead->_next);  //[1]
  }

  iterator end(){
    return iterator(_phead);
  }

  const_iterator begin() const{ //[3]
    return const_iterator(_phead->_next);
  }

  const_iterator end() const{
    return const_iterator(_phead);
  //......
  }

解释:

  • [1] 这里用指向节点的指针构造匿名对象做返回值。
  • [2] iterator和const_iterator的后两个模版参数不同是两个不同的实例化类型
    • iterator类的*和->操作返回普通引用和普通指针。
    • const_iterator类的*和->操作返回const引用和const指针。
  • [3] 这里的const用于修饰this指针,即调用函数的对象是const Mylist
    1. const对象返回const迭代器,const迭代器的*和->操作的返回值是只读的const引用和const指针。
    2. 也就是说,const迭代器指向的节点不能修改。(类似于常量指针 const int *ptr)

结构图示:
在这里插入图片描述


2.2 operator* & operator->

  Ref operator*() const{ //[3]
    return _pnode->_data; //[1]
  }

  Ptr operator->() const{ //[3]
    return &(_pnode->_data); //[2]
  }

//下面的代码使用->访问对象的成员:
  struct Pos{
    int _row;
    int _col;
    Pos(int row = 0, int col = 0)
      :_row(row),
      _col(col)
    {}
  };

  void Test7(){
    cout << "Test7: " << endl;
    Mylist<Pos> mlt; //链表存储的数据类型是自定义类型
    mlt.push_back(Pos(10,10));
    mlt.push_back(Pos(20,20));

    for(auto it = mlt.begin(); it != mlt.end(); ++it)
    {
      cout << it->_row << ":" << it->_col << endl; //[4]
    }
  }

解释:

  • [1] 迭代器解引用返回节点数据_data的引用。迭代器解引用后可以直接访问修改节点数据。
  • [2] 迭代器的operator->返回节点数据_data的地址。专为自定义类型的_data使用,可以直接使用迭代器加->的方式访问_data的成员。
  • [3] *和->操作涉及普通对象和const对象的权限问题,需要传入Ref和Ptr两个模版参数,泛型返回值
  • [3] 普通对象传入普通引用和普通指针,*和->操作后返回的引用和指针可读可写;const对象传入const引用和const指针,*和->操作后返回的引用和指针只能读不能写
  • [4] 这里其实应该有两个箭头it->->_row
    1. 第一个箭头调用operator->函数返回_data对象的地址。
    2. 第二个箭头用于访问_data对象的成员_row。
    3. 为了提高可读性,在设计c++语法时省略了一个箭头。这里的操作编译器会做特殊处理。

2.3 operator== & operator!=

  bool operator!=(const iterator &it) const{ //[1]
    return _pnode!=it._pnode;
  }

  bool operator==(const iterator &it) const{ //[1]
    return _pnode==it._pnode;
  }

解释:

  • [1] 这里要区分清楚const iterator对象和const Mylist对象:
  • [1] 这里的最后一个const用于修饰this指针,即调用的函数的对象是const iterator
    1. const iterator表示其成员变量_pnode不能改变,也就是说迭代器的指向不能变。(类似于指针常量 int *const ptr)
    2. const iterator可以进行解引用,->访问成员变量,进行==/!=的条件判断等。但不能进行++等操作。

2.4 前置++ & 后置++

  iterator& operator++(){
    _pnode = _pnode->_next; //[1]
    return *this;
  }

  iterator operator++(int){
    iterator tmp(*this); //[2]
    _pnode = _pnode->_next;
    return tmp;
  }

解释:

  • [1] list迭代器的++操作不同于vector迭代器(原生指针直接++),需要访问节点的_next将指针指向下一个节点。
  • [2] 后置++需要先拷贝一个临时迭代器,用于返回++之前的结果。因此需要传值返回。

提示:还有前置--和后置--,只需要访问节点的_prev将指针指向前一个节点即可。


三、默认成员函数

3.1 构造

  void empty_initialize(){ //[1]
    _phead = new Node;
    _phead->_next = _phead;
    _phead->_prev = _phead;
  }

  Mylist(){
    empty_initialize();  
  }

  template <typename InputIterator>
  Mylist(InputIterator first, InputIterator last){
    empty_initialize(); //[2]
    while(first!=last)
    {
      push_back(*first);
      ++first;
    }
  }

解释:

  • [1] 这里将空初始化单独拿出来,是为了方便下面多处进行复用
  • [2] 交换前必须要进行空初始化,否则会访问野指针崩溃

3.2 拷贝构造 & 赋值重载

  //void swap(Mylist<T> &mlt)
  void swap(Mylist &mlt){ //[1]
    std::swap(_phead, mlt._phead);
  }
  //Mylist<T>(const Mylist<T> &mlt)
  Mylist(const Mylist &mlt){ //[1]
    empty_initialize(); //交换前必须要进行空初始化,否则会访问野指针崩溃
    Mylist tmp(mlt.begin(), mlt.end());
    swap(tmp);
  }

  Mylist& operator=(Mylist mlt){
    swap(mlt);
    return *this;
  }

解释:

  • [1] 定义类成员函数时同类类型的类型名可以省略模版参数,但要注意:
    1. 不同类的类型名不可以省略模版参数。
    2. 在类外定义函数必须写明模版参数。

3.3 析构

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

  ~Mylist(){
    clear(); //清除所有数据节点
    delete _phead; //释放哨兵位节点
  }

注意:一定记得释放哨兵位节点哦!


四、插入删除

4.1 insert

  iterator insert(iterator pos, const T &val){
    Node *cur = pos._pnode; //[1]
    Node *prev = cur->_prev;
    Node *newnode = new Node(val);
    prev->_next = newnode;
    newnode->_prev = prev;
    newnode->_next = cur;
    cur->_prev = newnode;
    return iterator(newnode); //[2]
  }

解释:

  • [1] list_iterator使用struct,Mylist类可以直接访问其成员。
  • [2] 返回新插入节点的迭代器。

4.2 push_back & push_front

  void push_back(const T &val){
    insert(end(), val);
  }

  void push_front(const T &val){
    insert(begin(), val);
  }

4.3 erase

  iterator erase(iterator pos){
    assert(pos != end()); //[1]
    Node *cur = pos._pnode;
    Node *prev = cur->_prev;
    Node *next = cur->_next;
    next->_prev = prev;
    prev->_next = next;
    delete cur;
    return iterator(next); //[2]
  }

解释:

  • [1] 不能删除哨兵位节点。
  • [2] 返回删除节点的下一个节点的迭代器。

4.4 pop_back & pop_front

  void pop_back(){
    assert(_phead->_next != _phead); //[1]
    erase(--end());
  }

  void pop_front(){
    assert(_phead->_next != _phead);
    erase(begin());
  }

注意:pop之前要判空!

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

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

相关文章

截图自动添加水印(macOS/windows)

文章目录 1. 截图自动加水印1.1. windows1.2. macOS 2. 对已有图像批量加水印2.1 windows2.2 macOS 1. 截图自动加水印 1.1. windows 直接看这篇文章&#xff0c;一键截图自动生成水印/自动签名主要就是使用一个叫 SPX 的软件 1.2. macOS 其实apple的操作系统&#xff0c;i…

7、行为模式-观察者模式

一、观察者模式 当对象间存在一对多关系时&#xff0c;则使用观察者模式&#xff08;Observer Pattern&#xff09;。比如&#xff0c;当一个对象被修改时&#xff0c;则会自动通知依赖它的对象。观察者模式属于行为型模式。 介绍 意图&#xff1a;定义对象间的一种一对多的…

第十章:C语言的调试

很多小伙伴刚开始听到C语言的调试&#xff0c;这是个啥&#xff0c;表示很怀疑&#xff0c;敲代码不就是直接就是干嘛&#xff0c;结果很多小白们&#xff0c;一运行错误多的数都数不过来。就开始这改改&#xff0c;那删删&#xff0c;莫名奇妙就运行成功了。到最后都不知道到底…

WEB自动化测试,一定得掌握的8个核心知识点

​ 编辑 写在前面 使用 cypress 进行端对端测试&#xff0c;和其他的一些框架有一个显著不同的地方&#xff0c;它使用 JavaScript 作为编程语言。 传统主流的 selenium 框架是支持多语言的&#xff0c;大多数 QA 会的 python 和 Java 语言都可以编写 selenium 代码&#xff0…

Android逆向之雷速体育(360加固)

本教程仅限于学术探讨&#xff0c;也没有专门针对某个网站而编写&#xff0c;禁止用于非法用途、商业活动等&#xff0c;否则后果自负。如有侵权&#xff0c;请告知删除&#xff0c;谢谢&#xff01; 目录 一、攻克难点 二、分析接口 三、结束 一、攻克难点 本期app难点&am…

2023远程控制软件排行榜

对远程控制软件的排名可能是主观的&#xff0c;因为它取决于个人的需求和偏好。 但是&#xff0c;这里列出了因其功能和性能而广受认可的流行远程控制软件&#xff1a; TeamViewer TeamViewer 是一款成熟的远程控制软件&#xff0c;以其易用性和广泛的功能而闻名。 它支持远程…

君子签电子签章助推企业实现印章“集中统一管控”和“分级分权管理”

对于中大型企业来说&#xff0c;印章管理一直以来都是个“老大难”问题。 在中大型企业&#xff0c;印章数量多&#xff0c;大多分散在各个分/子公司的各个部门&#xff0c;企业印章无法统一进行有效管理。印章分散各地&#xff0c;异地分/子公司跨区域盖章难&#xff1b;分/子…

java servlet 二手物品交易平台Myeclipse开发mysql数据库web结构jsp编程计算机网页项目

一、源码特点 java servlet 二手物品交易平台是一套完善的java web信息管理系统&#xff0c;对理解JSP java编程开发语言有帮助 系统采用 serlvetdaobean 模式开发 &#xff0c;系统具有完整的源代码和数据 库&#xff0c;系统主要采用B/S模式开发。开发环境为TOMCAT7.0,My…

Allure在本地不安装allure服务的情况下打开Allure Html报告

前言 我们使用pytestallure生成Allure测试报告后&#xff0c;需要发给领导查看报告的详细信息。此时我们通过将allure生成的html报告压缩成压缩包后发送给领导&#xff0c;但是领导电脑由于没有安装Allure服务&#xff0c;打开会全部显示“Loading”&#xff0c; 无法查看到报…

信创办公–基于WPS的PPT最佳实践系列 (插入表格或图标)

信创办公–基于WPS的PPT最佳实践系列 &#xff08;插入图表或图标&#xff09; 目录 项目背景操作步骤1、插入表格2、实现放映功能 项目背景 当表格数据过大时&#xff0c;将其复制粘贴到PPT上时会显得插入的表格内容小、拥挤&#xff0c;使PPT看起来不美观。这个问题我们可以…

《微服务实战》 第十七章 Redis下载与安装

前言 Redis 全称 Remote Dictionary Server(即远程字典服务),它是一个基于内存实现的键值型非关系(NoSQL)数据库,由意大利人 Salvatore Sanfilippo 使用 C 语言编写。 与 SQL 型数据库截然不同,Redis 没有提供新建数据库的操作,因为它自带了 16 (0—15)个数据库(默…

牛客 BM76 正则表达式匹配 【动态规划】

描述 请实现一个函数用来匹配包括.和*的正则表达式。 1.模式中的字符.表示任意一个字符 2.模式中的字符*表示它前面的字符可以出现任意次&#xff08;包含0次&#xff09;。 在本题中&#xff0c;匹配是指字符串的所有字符匹配整个模式。例如&#xff0c;字符串"aaa"…

可算是熬出头了,测试6年,费时8个月,入职阿里,涨薪14K

前言 你的努力&#xff0c;终将成就无可替代的自己。 本科毕业后就一直从事测试的工作&#xff0c;和多数人一样&#xff0c;最开始从事点点点的工作&#xff0c;看着自己的同学一步一步往上走&#xff0c;自己还是在原地踏步&#xff0c;说实话这不是自己想要的状态。 一年半…

GPT_academic详细使用介绍与配置—可润色,翻译,解释代码(GPT助力学术,借助New Bing可实现免费使用)

&#x1f951; Welcome to Aedream同学 s blog! &#x1f951; 文章目录 绪论进入项目地址下载并解压到本地配置python3.11版本的虚拟环境安装运行所需的依赖库配置config配置代理文件配置GPT的api&#xff08;可选&#xff0c;按token计费&#xff09;配置NewBing的cookie&…

课程表 II:拓扑i排序

Problem: 210. 课程表 II 文章目录 思路解题方法1&#xff1a;首先新建一个inDegree数组用来存放所有的点的入度&#xff1a;int[] inDegree new int[numCourses];2&#xff1a;然后遍历所有子数组将所有点及其入度存进去&#xff0c;这道题就是课程号本身为坐标&#xff0c;对…

ASEMI代理长电可控硅PCR606:性能特点与应用领域

编辑-Z 可控硅&#xff08;Thyristor&#xff09;是一种半导体器件&#xff0c;具有高功率、高效率、高可靠性等优点。PCR606是一款常用的可控硅型号&#xff0c;广泛应用于各种电子设备中。本文将详细介绍PCR606的性能特点、工作原理以及在各个领域的应用。 一、PCR606性能特…

【算法学习系列】06 - 利用二分法查找有序数组中的某个数 num

文章目录 二分法说明实现 二分法验证实现暴力算法对数器使用验证结果 二分法 说明 二分法是一种常用的算法&#xff0c;也称为折半查找或二分查找。它适用于已经有序的数组中&#xff0c;通过将数组从中间划分成两个部分&#xff0c;每次根据目标值与中间值的大小比较来确定下…

基于InVEST模型的人类活动、重大工程生态成效评估、论文写作

查看原文>>>基于生态系统服务&#xff08;InVEST模型&#xff09;的人类活动、重大工程生态成效评估、论文写作 【学习目标】&#xff1a; 1) 采用InVEST模型&#xff0c;掌握产水&#xff08;包括水源涵养&#xff09;、碳存储&#xff08;包括固碳&#xff09;、土…

2023年最新苹果账号更改/注册为美区账号及免国外支付购买和充值美区App Store礼品卡教程

平时大家在使用IPhone手机时,应该有遇到软件明明是在App Store里上架的,但自己无论如何也搜索不到的情况吧!这种就是因为应用没有上架到你苹果账号所在的国家地区才导致的搜不到无法下载。比如:部分的新闻类、代[过滤]理类、游戏类应用以及Chat GPT等都无法在国区账号中搜索…

基于GPS+IMU的卡尔曼滤波融合定位算法MATLAB代码

资源地址&#xff1a; 基于GPSIMU的卡尔曼滤波融合定位算法MATLAB代码资源-CSDN文库 主要内容&#xff1a; 基于GPSIMU的卡尔曼滤波融合定位算法仿真,其中惯导用来进行状态预测,GPS用来滤波矫正&#xff0c;用于GPSIMU的卡尔曼滤波融合定位算法算法编程学习&#xff01;&…