C++ STL 之 list 的模拟实现

news2024/11/28 2:38:08

文章目录

  • 📍前言
  • 🌈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/524577.html

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

相关文章

[MAUI程序设计]界面多态与实现

文章目录 需求一&#xff1a;在不同设备上使用不同 UI 外观定义控件 UI 外观基于平台自定义配置 需求二&#xff1a;在不同数据类别中使用不同的 UI 外观定义视图 UI 外观创建数据模板创建选择器定义数据 需求三&#xff1a;在不同数据状态中使用不同的 UI 外观使用绑定模型更改…

【腾讯云FinOps Crane 集训营】Crane 助力云原生用户充分发挥云上资源的最大价值,帮助企业降本增效之利器

文章目录 Crane 助力云原生用户充分发挥云上资源的最大价值&#xff0c;帮助企业降本增效之利器前言云上资源成本虚高的现状一、云原生的崛起&#xff1a;服务上云二、服务上云的魔咒&#xff1a;服务健壮了&#xff0c;钱袋子却空了三、钱都去哪了&#xff1a;云资源无序投入的…

golang 微服务的负载均衡

上次我们说了一下 微服务的容错处理&#xff0c;是用的 断路器 这一次我们来一起看看 微服务的负载均衡是如何做的 负载均衡 负载均衡是什么呢&#xff1f; 他能够将大量的请求&#xff0c;根据负载均衡算法&#xff0c;将不同的请求分发到多台服务器上进行处理&#xff0c…

Git Mac设置系统命令别名和Git命令别名

有时候git命令的别名过长要如何&#xff0c;在命令行不方便输入&#xff0c;这时候我们可以设置命令别名。 设置系统命令别名 设置系统命令别名可以在.bash_profile文件中配置&#xff0c;这个文件也是我们经常配置环境变量的地方&#xff0c;这个文件本身是不可见的&#xf…

rtp h264 发送和接收程序的问题

目的 为了自己写一个投屏协议&#xff0c;目前重新启用rtp协议&#xff0c;使用rtp协议直传效率最高&#xff0c;并且使用的是udp ffmpeg 发送rtp ffmpeg的rtp发送时一般把sps和pps放在一个包里面&#xff0c;写接收代码的时候要注意&#xff0c;在单包里面可以直接接收到两…

第一章: Mybatis-Plus 之了解

目录 1.1&#xff1a;Mybatis-Plus介绍 1.2&#xff1a;代码及文档地址 1.3&#xff1a;特性 1.4&#xff1a;架构 1.5&#xff1a;作者 下面的图文来自于官方的介绍 1.1&#xff1a;Mybatis-Plus介绍 Mybatis-Plus 是一个 Mybatis 的增强工具&#xff0c;在Mybatis的基…

Seata之 Win系统和 Linux系统搭建

文章目录 1 Seata搭建1.1 Linux环境搭建1.1.1 准备工作1.1.2 下载1.1.3 建表1.1.4 配置 nacos1.1.4.1 新建命名空间1.1.4.2 上传配置至Nacos配置中心1.1.4.3 不上传而使用配置 1.1.5 修改 appplication.yml1.1.5.1 seata.store1.1.5.2 seata.config1.1.5.3 seata.registry 1.1.…

多模态:InstructBLIP

多模态&#xff1a;InstructBLIP IntroductionMethoddatasetInstruction-aware 视觉提取架构Dataset Balance 实验参考 Introduction 作者表示&#xff0c;与nlp任务不同&#xff0c;多模态任务由于引入额外的视觉输入&#xff0c;它的任务更加多样化&#xff0c;这似的联合多…

Immich让你从此告别百度网盘备份手机照片

一. Immich 是什么 Immich是一个开源的图片自托管服务&#xff0c;它能实现类似于百度网盘的照片自动备份、分类等功能&#xff0c;它同时提供了Web管理页面&#xff0c;和移动端APP&#xff0c;可以轻松备份手机中的照片至家庭服务器中。这一应用也在很多群辉玩家中用于替代“…

在 Windows 上安装 Helm包

一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡&#xff0c;让我们一起学习在 Windows 上安装 Helm包。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三连) 二、 Helm是什么 Helm是Kubernetes的包管理工具&#xff0c;类似于centos的yum&#xff0c;能够快速查找、下载和安装…

(2022 EMNLP)结合面部表情的情感分析

论文题目&#xff08;Title&#xff09;&#xff1a;Face-Sensitive Image-to-Emotional-Text Cross-modal Translation for Multimodal Aspect-based Sentiment Analysis 研究问题&#xff08;Question&#xff09;&#xff1a;面向面部敏感的图像-情感-文本翻译的跨模态的多…

【开源项目】TinyId 全网最好的分布式ID生成系统的源码解析

TINYID介绍 项目地址&#xff1a;https://github.com/didi/tinyid Tinyid是滴滴开发的一款分布式ID系统&#xff0c;Tinyid是在美团&#xff08;Leaf&#xff09;的leaf-segment算法基础上升级而来&#xff0c;不仅支持了数据库多主节点模式&#xff0c;还提供了tinyid-client客…

23种设计模式之适配器模式(Adapter Pattern)

前言&#xff1a;大家好&#xff0c;我是小威&#xff0c;24届毕业生&#xff0c;在一家满意的公司实习。本篇文章将23种设计模式中的适配器模式&#xff0c;此篇文章为一天学习一个设计模式系列文章&#xff0c;后面会分享其他模式知识。 如果文章有什么需要改进的地方还请大佬…

[GYCTF2020]EasyThinking

功能&#xff1a;登录&#xff0c;注册&#xff0c;搜索 回显登录用户名&#xff0c;搜索历史 简单测试搜索历史发现可能不存在sql注入 www.zip下载源码 访问一个不存在的路径&#xff0c;爆出 thinkphp的框架&#xff0c;版本是6.0.0 参考&#xff1a;ThinkPHP 6.0.1 漏洞…

MyBatis操作数据库实现增删改查

创建数据库 语句要分别执行 CREATE DATABASE mybatis;USE mybatis;CREATE TABLE user(id INT(10) NOT NULL PRIMARY KEY,name VARCHAR(20) DEFAULT NULL,INSERT INTO user(id,name,pwd) VALUES (1,张三,123456), (2,李四,121212), (3,王五,1314520) 搭配环境 1、在pojo包创…

25.基于混合整数规划方法的微网电池储能容量优化配置

关键词&#xff1a;储能容量优化 储能配置 微网 编程语言&#xff1a;matlab 主题&#xff1a;基于混合整数规划方法的微网电池储能容量优化配置 主要内容&#xff1a; 本代码目的为实现微电网内电池容量的优化配置&#xff0c;目标函数为配置过程中整体的运行成本最小或…

【Spring】Spring AOP面向切面编程

文章目录 什么是Spring AOP&#xff1f;为什么要使用AOP&#xff1f;AOP相关组成的概念切面切点通知连接点 Spring AOP实现创建切面创建切点创建通知创建连接点示例演示 Spring AOP的实现原理 什么是Spring AOP&#xff1f; 想要知道Spring AOP&#xff0c;就得先了解AOP AOP是…

【周期信号】工程测试-数据处理-信号分析课程试题:周期信号与周期信号相加,所得信号一定是周期信号吗?

一、问题分析 某课程的作业题中&#xff0c;有下面的一种题目&#xff0c;判断两个周期信号相加&#xff0c;是否是周期信号&#xff0c;以及计算周期长短是多少。 非常显然&#xff0c;1、3、4题都很容易判断。 第2题&#xff0c;我们重点分析。 二、网上的错误论述 在百…

Vivado运用 Language Template 来创建set_input_delay/set_output...

时序约束中的 set_input_delay/set_output_delay 约束一直是一个难点&#xff0c;无论是概念、约束值的计算&#xff0c;还是最终的路径分析&#xff0c;每一次都要费一番脑子。Vivado为方便用户创建输入输出接口的约束&#xff0c;整理出了一套非常实用的InputDelay/Output De…

【C++】类与对象(3)

【C】类与对象&#xff08;3&#xff09; 作者&#xff1a;爱写代码的刚子 时间&#xff1a;2023.5.9 本篇博客干货比较多&#xff0c;主要是对类和对象知识的进一步加深&#xff0c;可能有点晦涩。主要介绍的内容为&#xff1a;深入构造函数&#xff0c;初始化列表&#xff0c…