【C++ STL】 list 模拟实现

news2024/11/27 2:38:38

文章目录

  • 📍前言
  • 🌈STL之list的模拟实现
    • 🎈list_node节点的定义
    • 🎈iterator迭代器
      • 🕯️构造函数
      • 🕯️*it
      • 🕯️->
      • 🕯️it++/++it
      • 🕯️it--/--it
      • 🕯️!= / ==
    • 🎈list类
      • 🕯️begin()/end()
      • 🕯️const_begin()/const_end()
      • 🕯️构造函数
      • 🕯️insert函数
      • 🕯️erase函数
      • 🕯️push_back函数
      • 🕯️push_front函数
      • 🕯️pop_back函数
      • 🕯️pop_front函数
    • 🎈源码
  • 📍后记

📍前言

本篇将学习 list 的模拟实现,它的主要难点在与迭代器的模拟。

🕺作者: 迷茫的启明星
专栏:《C++初阶》
😘欢迎关注:👍点赞🙌收藏✍️留言

🏇码字不易,你的👍点赞🙌收藏❤️关注对我真的很重要,有问题可在评论区提出,感谢阅读!!!

持续更新中~

🌈STL之list的模拟实现

🎈list_node节点的定义

  • 这里需要使用结构体
  • 在这里需要使用模板,因为数据类型是不确定的
  • 双向链表的结构包括:两个指针和数据
  • 我们在定义节点时还需要初始化
template<class T>
struct list_node
{
    T _data;
    list_node<T>* _next;
    list_node<T>* _prev;
    
	//初始化
    list_node(const T& val = T())
        //为什么传引用?
        //传引用减少拷贝
        //初始化的值是T的默认构造函数,不能传0
        //T可能是string类型或其他类型
    :_data(val)
    ,_next(nullptr)
    ,_prev(nullptr)
    {}
};

🎈iterator迭代器

迭代器可以理解成指针,但是它比指针复杂多了,指针无外乎地址,而迭代器则是不是指针,却要让它实现指针的功能:++、–、*等操作

🕯️构造函数

我们构造好了一个_node,但是没有给他赋值,就等着外面传过来一个node,把node给给里面的_node即可。

__list_iterator(Node* node)
    :_node(node)
    {}

🕯️*it

我们把迭代器比作指针,方便理解,这个函数就是对“指针”解引用,拿到它的值。注意结果返回引用,减少拷贝

Ref operator*()
{
   return _node->_data;
}

🕯️->

拿到节点的值的地址,因为返回的是一个地址,所以要用指针接收。结果返回一个指针。

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

🕯️it++/++it

为了实现类似指针的效果,迭代器前置++和后置++分别就要返回没改变的值和改变了的值。

++it
迭代器向后走一步,返回加过的值(还是一个迭代器)
it++
迭代器向后走一步,返回没加过的值(还是一个迭代器)

//++it
iterator& operator++()
{
   _node = _node->_next;
   return *this;
}

// it++
iterator operator++(int)
{
   iterator tmp(*this);//保存原来的值
   _node = _node->_next;
   return tmp;
}

🕯️it–/–it

原因与上同

// --it
iterator& operator--()
{
   _node = _node->_prev;
   return *this;
}

// it--
iterator operator--(int)
{
   iterator tmp(*this);
   _node = _node->_prev;
   return tmp;
}

🕯️!= / ==

仅需判断里面的this是否一样即可

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

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

🎈list类

🕯️begin()/end()

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

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

🕯️const_begin()/const_end()

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

🕯️构造函数

list()
{
   _head = new Node;
   _head->_next = _head;
   _head->_prev = _head;
}

🕯️insert函数

双向链表的插入,是不是非常简单呢?只需要将新的节点的指针与插入位置前后指针相连即可。

iterator insert(iterator pos, const T& x)
{
   Node* cur = pos._node;
   Node* prev = cur->_prev;

   Node* newnode = new Node(x);
    
   prev->_next = newnode;
   newnode->_prev = prev;
   newnode->_next = cur;
   cur->_prev = newnode;

   return iterator(newnode);
}

🕯️erase函数

与Insert函数一样,把需要删除的节点的前后节点绑在一起就完成了。

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);
}

🕯️push_back函数

它有两种方式,既可以自己来实现每一步,也可以直接复用insert函数在末尾插入

void push_back(const T& x)
{
   //Node* tail = _head->_prev;
   //Node* newnode = new Node(x);

    _head          tail  newnode
   //tail->_next = newnode;
   //newnode->_prev = tail;
   //newnode->_next = _head;
   //_head->_prev = newnode;

   insert(end(), x);
}

🕯️push_front函数

它和上面push_back函数讲的一样,不过这是在开头插入

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

🕯️pop_back函数

复用erase函数

void pop_back()
{
   erase(--end());
}

🕯️pop_front函数

复用erase函数

void pop_front()
{
   erase(begin());
}

🎈源码

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

      list_node(const T& x = T())
         :_data(x)
         , _next(nullptr)
         , _prev(nullptr)
      {}
   };

   template<class T, class Ref, class Ptr>
   struct __list_iterator
   {
      typedef list_node<T> Node;
      typedef __list_iterator<T, Ref, Ptr> iterator;

      typedef bidirectional_iterator_tag iterator_category;
      typedef T value_type;
      typedef Ptr pointer;
      typedef Ref reference;
      typedef ptrdiff_t difference_type;


      Node* _node;

      __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;
      }

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

      // ++it
      iterator& operator++()
      {
         _node = _node->_next;
         return *this;
      }
      
      // it++
      iterator operator++(int)
      {
         iterator tmp(*this);
         _node = _node->_next;
         return tmp;
      }

      // --it
      iterator& operator--()
      {
         _node = _node->_prev;
         return *this;
      }

      // it--
      iterator operator--(int)
      {
         iterator tmp(*this);
         _node = _node->_prev;
         return tmp;
      }
   };

   template<class T>
   class list
   {
      typedef list_node<T> Node;
   public:
      typedef __list_iterator<T, T&, T*> iterator;
      typedef __list_iterator<T, const T&, const T*> const_iterator;

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

      const_iterator end() const
      {
         return const_iterator(_head);
      }

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

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

      list()
      {
         _head = new Node;
         _head->_next = _head;
         _head->_prev = _head;
      }

      void push_back(const T& x)
      {
         //Node* tail = _head->_prev;
         //Node* newnode = new Node(x);

          _head          tail  newnode
         //tail->_next = newnode;
         //newnode->_prev = tail;
         //newnode->_next = _head;
         //_head->_prev = newnode;

         insert(end(), x);
      }

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

      iterator insert(iterator pos, const T& x)
      {
         Node* cur = pos._node;
         Node* prev = cur->_prev;

         Node* newnode = new Node(x);

         prev->_next = newnode;
         newnode->_prev = prev;
         newnode->_next = cur;
         cur->_prev = newnode;

         return iterator(newnode);
      }

      void pop_back()
      {
         erase(--end());
      }

      void pop_front()
      {
         erase(begin());
      }

      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);
      }

   private:
      Node* _head;
   };
}

📍后记

本文主要介绍了STL中双向链表list的模拟实现。

✨通过结构体定义list_node节点,通过模板实现数据类型的不确定性,并对节点进行初始化。

✨利用迭代器iterator实现指针的功能,包括构造函数、解引用、*it、->、前后置++/–、!=和==等操作。

✨在list类中,通过双向链表实现了begin()/end()、const_begin()/const_end()、insert、erase、push_back、push_front、pop_back和pop_front等函数。

✨其中,push_back和push_front函数可以复用insert函数,在末尾和开头插入元素。

✨pop_back和pop_front函数可以复用erase函数,删除末尾和开头的元素。

感谢大家支持!!!

respect!

下篇见!

在这里插入图片描述

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

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

相关文章

Web开发介绍

Web开发介绍 1 什么是web开发 Web&#xff1a;全球广域网&#xff0c;也称为万维网(www World Wide Web)&#xff0c;能够通过浏览器访问的网站。 所以Web开发说白了&#xff0c;就是开发网站的&#xff0c;例如下图所示的网站&#xff1a;淘宝&#xff0c;京东等等 那么我们…

如何使用sbvadmin进行私有化部署的代码开发

前言 本文主要讲述如何使用sbvadmin进行私有化部署的代码开发&#xff0c;这里我们用的私有化仓库是gitee&#xff0c;当然你也可以用自己搭建的gitlab来做&#xff0c;原理差不多。 一、新建仓库 1.后端api 导入后端仓库&#xff1a;https://github.com/billyshen26/sbvadmi…

08- 算法解读 Mask R-CNN (目标检测)

要点&#xff1a; Mask R-CNN 解读 参考资料&#xff1a;vision/references/detection at main pytorch/vision GitHub 四 Mask R-CNN 基本信息 4.1 环境配置 Python3.6/3.7/3.8Pytorch1.10或以上pycocotools(Linux:pip install pycocotools; Windows:pip install pycoco…

Zigbee物联网应用与开发复习汇总(附某高校期末真题试卷)

文章目录 一、知识梳理二、编程实战三、高校真题A卷B卷 一、知识梳理 1. Zigbee、蓝牙、IEEE802.11b&#xff08;WiFi&#xff09;标准都是工作在2.4G频段的无线通信标准&#xff1b;Zigbee主要用在短距离无线控制系统&#xff0c;传输少量的控制信息&#xff1b; 2. 短距离无…

【Linux系统】Linux进程信号详解

Linux进程信号 0 引言1 认识信号1.1 什么是信号1.2 发送信号的本质1.3 信号的处理 2 信号的产生2.1 键盘产生2.2 调用系统函数向进程发送信号2.3 由软件条件产生信号2.4 硬件异常产生信号 3 信号的保存4 信号的处理5 总结 0 引言 本篇文章会从Linux信号的产生到信号的保存&…

rtl仿真器-epicsim安装和测试

前言 epicsim 是芯华章的仿真器&#xff0c;基于iverilog 据说速度快两倍。 源码 github https://github.com/x-epic/EpicSim gittee https://gitee.com/x-epic/ 公司网站 https://www.x-epic.com/index.html#/en/developer 维护中了 安装 依赖 有些 apt-get install 就可…

【2023秋招】2023华为od4.28三道题

2023大厂笔试模拟练习网站&#xff08;含题解&#xff09; www.codefun2000.com 最近我们一直在将收集到的各种大厂笔试的解题思路还原成题目并制作数据&#xff0c;挂载到我们的OJ上&#xff0c;供大家学习交流&#xff0c;体会笔试难度。现已录入200道互联网大厂模拟练习题&…

测试开发工程师到底是做什么的?你真的知道吗

目录 一二三线互联网公司对测试开发工程师的要求&#xff1a; 测试开发工程师的具体职责&#xff1a; 不要迷失方向 总结&#xff1a; 测试开发工程师必看视频教程&#xff1a; 一二三线互联网公司对测试开发工程师的要求&#xff1a; 现在很多测试的同事对测试开发工程师…

实现第一个服务器版本的表白墙程序

文章目录 表白墙前言1. 环境部署1.1 创建maven项目1.2 引入依赖1.3 创建目录结构1.4 部署程序 2. 前端页面3. 后端实现3.1 后端逻辑3.2 后端代码 表白墙 前言 基于MySQL数据库和servlet实现的前后端交互的服务器版本表白墙。在页面输入表白内容&#xff0c;在本地通过数据库存…

大数据Doris(二十三):Rollup物化索引作用和注意点

文章目录 Rollup物化索引作用和注意点 一、Rollup物化索引作用 1、改变索引 2、聚合数据

操作系统王道考研学习(二)操作系统的特征

目录 操作系统的特征&#xff1a;并发、共享、虚拟、异步 操作系统中并发为什么那么重要&#xff1f; 讲一讲多道程序技术 介绍一下空分复用技术 异步下程序是走走停停的 操作系统的特征&#xff1a;并发、共享、虚拟、异步 并发和共享 虚拟和异步 &#xff08;为什么要并…

物联网的体系架构

物联网中常见的计算模式&#xff1a;云计算、边缘计算、雾计算等 云计算&#xff1a;一种利用互联网实现随时随地、按需、便捷地使用共享计算设施、存储设备、应用程序等资源的计算模式。边缘计算&#xff1a;在靠近物或数据源头的网络边缘侧&#xff0c;融合网络、计算、存储…

本周前半周总结

刷题刷了六道 青训营视频补看 软件杯项目素材收集&#xff0c;首页制作ing 前面这六道题的题解&#xff1a; 题目1&#xff1a; 这是个交互题&#xff0c;目前遇到的交互题都是用二分解决的。 本题使用二分精准定位拥有重量为2的石头的堆。 为避免时间超限&#xff0c;应该再…

k8s1.20版本部署RabbitMQ集群(持久化)——2023.05

文章目录 一、集群概况二、RabbitMQ集群部署2.1 安装NFS2.2 创建storageclass存储类2.3 部署RabbitMQ集群2.4 测试 一、集群概况 主机规划 节点IPk8s-master1192.168.2.245k8s-master2192.168.2.246k8s-master3192.168.2.247k8s-node1192.168.2.248NFS、Rancher192.168.2.251…

阿里巴巴 菜鸟Java面经

目录 1.ArrayList和LinkedList的区别2.两个各自装啥数据合适3.final和finally的区别4.catch里面有个return&#xff0c;finally执行不执行5.线程的创建方式6.ThreadLocal7.序列化8.抽象类和接口的区别9.数据库的四大特性10.事务的一致性是啥11.事务的隔离级别12.可重复读是个啥…

inspect.exe安装使用

官网下载 https://developer.microsoft.com/zh-cn/windows/downloads/windows-sdk/ 官网教程 https://learn.microsoft.com/zh-cn/windows/win32/winauto/inspect-objects 要求 系统要求 Windows SDK 具有以下最低系统要求&#xff1a; 支持的操作系统 Windows 10版本 150…

chatgpt赋能Python-pycharm如何跳过教程

PyCharm如何跳过教程&#xff1a;快速掌握Python编程 如果你是一个有10年python编程经验的工程师&#xff0c;那么你肯定不需要再从头开始学习python&#xff0c;更不需要花费大量时间来学习PyCharm的教程。你需要的是一个快速而高效地使用PyCharm的方法&#xff0c;以便能够更…

chatgpt赋能Python-pandas预处理

介绍 Pandas是一个强大的Python库&#xff0c;专门用于数据操作和分析。在数据处理和分析的过程中&#xff0c;Pandas是一个不可或缺的工具。它提供了简单而灵活的数据结构&#xff0c;如Series和DataFrame&#xff0c;这些数据结构可以帮助我们快速预处理数据。 本文将介绍P…

虚拟机 01 jdk环境的安装与配置

01.第一步&#xff1a;进入到工作目录中&#xff0c;然后将目录中所有的资源都删掉 &#xff0c;此处的工作目录/usr/local/src 使用的命令是rm -rf * 02.第二步&#xff1a;将windows系统的jdk8的安装文件上传到Linux中 直接在window界面中选中压缩文件拖到Linux命令行中 完…

区间预测 | MATLAB实现QGPR高斯过程分位数回归时间序列区间预测

区间预测 | MATLAB实现QGPR高斯过程分位数回归时间序列区间预测 目录 区间预测 | MATLAB实现QGPR高斯过程分位数回归时间序列区间预测效果一览基本介绍模型描述程序设计参考资料 效果一览 基本介绍 MATLAB实现QGPR高斯过程分位数回归时间序列区间预测 1.基于高斯过程回归&#…