【C++】list的模拟实现

🌇个人主页:平凡的小苏
📚学习格言:命运给你一个低的起点,是想看你精彩的翻盘,而不是让你自甘堕落,脚下的路虽然难走,但我还能走,比起向阳而生,我更想尝试逆风翻盘
🛸C++专栏C++内功修炼基地
> 家人们更新不易,你们的👍点赞👍和⭐关注⭐真的对我真重要,各位路 过的友友麻烦多多点赞关注。 欢迎你们的私信提问,感谢你们的转发! 关注我,关注我,关注我,你们将会看到更多的优质内容!!

在这里插入图片描述

一、list的介绍

  1. list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。

  2. list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。

  3. list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高效。

  4. 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率更好。

  5. 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这可能是一个重要的因素

二、迭代器的功能分类

1、单向迭代器:只能++,不能–。例如单链表,哈希表;

2、双向迭代器:既能++也能–。例如双向链表;

3、随机访问迭代器:能+±-,也能+和-。例如vector和string。

迭代器是内嵌类型(内部类或定义在类里)

三、list的迭代器失效问题

对于list:插入操作是不会失效的,但是删除操作是会失效的,因为删除操作会进行释放节点

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表

因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删

除节点的迭代器,其他迭代器不会受到影响

错误代码:

void TestListIterator1()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){// erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给//其赋值l.erase(it); ++it;}
}

修改正确的代码:

void TestListIterator()
{int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };list<int> l(array, array+sizeof(array)/sizeof(array[0]));auto it = l.begin();while (it != l.end()){l.erase(it++); // it = l.erase(it);}
}

四、list的迭代器模拟实现

4.1普通迭代器

迭代器的主要作用:解引用获取数据、++、–

我在模拟实现string、vector时都是用的原生指针,没有将迭代器用类进行封装,这是为了简易化,但是STL标准库也是用用类封装了迭代器。而我在模拟实现list迭代器时,不能用原生指针了,因为list的节点地址时不连续的,不能使用原生指针。

template <class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> Self;Node *_node;__list_iterator(Node *node): _node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}Self &operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self &operator--(){_node = _node->_prev;return *this;}bool operator!=(const Self &it)const{return _node != it._node;}bool operator==(const Self &it)const{return _node == it._node;}};

在这里插入图片描述

这是用类封装了迭代器的作用,while循环的判断条件在迭代器的类里面进行了运算符重载,解引用也进行了运算符重载 否则自定义类型是做不到用运算符的,这样做的目的是为了我们不暴露迭代器的底层实现给使用者,用统一的方式像指针一样就能访问迭代器,这就是封装的强大之处。

4.2 const迭代器

const迭代器的错误写法:

typedef __list_iterator<T> iterator;
const list<T>::iterator it=lt.begin();

因为typedef后,const修饰的是迭代器it,只能调用operator*(),调不了operator++()。(重载operator++()为const operator++()也不行,因为const版本++还是改变不了)

正确写法:想实现const迭代器,不能在同一个类里面动脑筋,需要再写一个const版本迭代器的类。

//用类封装const迭代器
template <class T>
struct __list_const_iterator
{typedef list_node<T> node;//用节点的指针进行构造__list_const_iterator(node* p):_pnode(p){}//迭代器运算符的重载const T& operator*()const{return _pnode->_data;}__list_const_iterator<T>& operator++()//返回值不要写成node*,因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对{//return _pnode->_next;//返回类型错误的_pnode = _pnode->_next;return *this;//返回的是迭代器}__list_const_iterator<T>& operator--(){_pnode = _pnode->_prev;return *this;}bool operator!=(const __list_const_iterator<T>& it)const{return _pnode != it._pnode;}
public:node* _pnode;//封装一个节点的指针
};typedef __list_const_iterator<T> const_iterator;

这样写会让代码显得冗余。STL库中是通过类模板多给一个参数来实现,这样,同一份类模板就可以生成两种不同的类型的迭代器(以下为仿STL库的模拟实现):

在这里插入图片描述

4.3 反向迭代器

反向迭代器的++就是正向迭代器的–,反向迭代器的–就是正向迭代器的++,因此反向迭代器的实现可以借助正向迭代器,即:反向迭代器内部可以包含一个正向迭代器,对正向迭代器的接口进行包装即可。

template <class Iterator, class Ref, class Ptr>
class ReverseIterator
{
public:typedef ReverseIterator<Iterator, Ref, Ptr> Self;ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp = _it;--tmp;return *tmp;}Ptr operator->(){return &operator*();}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator!=(const Self& rit)const{return _it != rit._it;}bool operator==(const Self& rit)const{return _it == rit.it;}
private:Iterator _it;
};

五、list和vector区别

vector与list都是STL中非常重要的序列式容器,由于两个容器的底层结构不同,导致其特性以及应用场景不同,其主要不同如下:

1.底层结构

list:带头结点的双向循环链表

vector:动态顺序表,一段连续空间

2.迭代器失效

list:带头结点的双向循环链表

vector:在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效

3.使用场景

list:大量插入和删除操作,不关心随机访问

vector:需要高效存储,支持随机访问,不关心插入删除效率

4.随机访问

list:不支持随机访问,访问某个元素效率O(N)

vector:支持随机访问,访问某个元素效率****O(1)

5.插入和删除

list:任意位置插入和删除效率高,不需要搬移元素,时间复杂度为O(1)

vector:任意位置插入和删除效率低,需要搬移元素,时间复杂度为O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低

六、list的模拟实现源码

#pragma once
#include <iostream>
#include <cassert>
#include <algorithm>
using namespace std;
namespace sqy
{template <class T>struct list_node{list_node(const T &val = T()): _prev(nullptr), _next(nullptr), _val(val){}list_node<T> *_prev;list_node<T> *_next;T _val;};template <class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> Self;Node *_node;__list_iterator(Node *node): _node(node){}Ref operator*(){return _node->_val;}Ptr operator->(){return &_node->_val;}Self &operator++(){_node = _node->_next;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self &operator--(){_node = _node->_prev;return *this;}bool operator!=(const Self &it)const{return _node != it._node;}bool operator==(const Self &it)const{return _node == it._node;}};template <class Iterator, class Ref, class Ptr>class ReverseIterator{public:typedef ReverseIterator<Iterator, Ref, Ptr> Self;ReverseIterator(Iterator it):_it(it){}Ref operator*(){Iterator tmp = _it;--tmp;return *tmp;}Ptr operator->(){return &operator*();}Self& operator++(){--_it;return *this;}Self& operator--(){++_it;return *this;}bool operator!=(const Self& rit)const{return _it != rit._it;}bool operator==(const Self& rit)const{return _it == rit._it;}private:Iterator _it;};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;typedef ReverseIterator<iterator, T&, T*> reverse_iterator;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}void empty_init(){_head = new Node;_head->_prev = _head;_head->_next = _head;_size = 0;}void swap(list<T> &lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}// list<T> &operator=(list<T> lt)// {//     if(this != &lt)//     {//         clear();//         for(auto& t : lt)//         {//             push_back(t);//         }//     }// }//现代写法赋值运算符重载list<T> &operator=(list<T> lt){swap(lt);return *this;}list(){empty_init();}list(const list<T> &lt){empty_init();for (auto &t : lt){push_back(t);}}~list(){clear();delete _head;_size = 0;}void push_back(const T &x){// Node * tail = _head->_prev;// Node * newnode = new Node(x);// tail->_next = newnode;// newnode->_prev = tail;// newnode->_next = _head;// _head->_prev = newnode;// ++_size;insert(end(), x);}void push_front(const T &x){// Node * tail = _head->_prev;// Node * newnode = new Node(x);// tail->_next = newnode;// newnode->_prev = tail;// newnode->_next = _head;// _head->_prev = newnode;// ++_size;insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator insert(iterator pos, const T &x){Node *newnode = new Node(x);Node *prev = pos._node->_prev;newnode->_prev = prev;prev->_next = newnode;newnode->_next = pos._node;pos._node->_prev = newnode;_size++;return pos;}iterator erase(iterator pos){assert(_size != 0);Node *prev = pos._node->_prev;Node *tail = pos._node->_next;Node *cur = pos._node;prev->_next = tail;tail->_prev = prev;delete cur;--_size;return pos;}// 链表小接口bool empty() const{return _head->_next == _head;}void clear(){iterator it = begin();while (it != end()){erase(it);++it;}}size_t size() const{return _size;}private:Node *_head;size_t _size = 0; // 元素个数};void Test_list1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;// list<int>::iterator it1 = lt2.begin();// while (it1 != lt2.end())// {//     cout << *it1 << " ";//     ++it1;// }// cout << endl;// list<int>::reverse_iterator rit = lt.rbegin();// while(rit != lt.rend())// {//     cout << *rit <<endl;//     ++rit;// }}
}

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

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

相关文章

5. MySQL - JDBC SQL 注入 博客系统(万字详解)

目录 1. 介绍 2. 使用 JDBC 连接数据库 2.1 如何使用 JDBC 连接数据库 2.2 导入的各个类 2.3 DataSource 对象的创建 2.4 从 DataSource 对象中得到 Connection 对象 2.5 创建 Statement 对象 2.6 从 ResultSet 中遍历每行结果&#xff0c;从每行中获取每列的值 2.7 代…

EIK+Filebeat+Kafka

目录 Kafka 概述 为什么需要消息队列&#xff08;MQ&#xff09; 使用消息队列的好处 消息队列的两种模式 Kafka 定义 Kafka 简介 Kafka 的特性 Kafka 系统架构 Partation 数据路由规则&#xff1a; 分区的原因 部署 kafka 集群 1.下载安装包 2.安装 Kafka 修改配…

B072-项目实战-用户模块--前台登录 三方登录

目录 前台登录-账号登录前端完成左上角显示用户信息配置前置拦截器、后置拦截器和不受限资源拦截器 三方登录-微信登录概述流程图用法代码实现步骤分析:实现准备代码前端login.htmlcallback.html 后端LoginController-微信登录LoginServiceImpl-微信登录解决回调域名不能跨域绑…

安达发|某大厂使用APS计划排程真实成功案例

在很多群里、朋友圈、公众号上可以看到&#xff0c;很多精益咨询老师认为&#xff0c;不仅ERP不啥用&#xff0c;APS更是无聊之举&#xff0c;而且肯定是用不好的。但&#xff0c;事实上可能还真不是这样的。 一个深圳的客户&#xff0c;用了APS以后&#xff0c;不仅装配的齐套…

桥梁监测需要哪些设备?

随着我国经济的发展&#xff0c;我国桥梁建设也迈上了新的台阶。截至2022年底&#xff0c;我国的公路桥梁总数达到了103.32万座。然而&#xff0c;随着在役桥梁使用时间的增长&#xff0c;承载能力受到荷载、环境以及结构退化等因素的影响&#xff0c;桥梁安全问题日益凸显。桥…

vue3和gin框架实现简单的断点续传

vue3和gin框架实现简单的断点续传 前端代码 Test.vue <template><div><inputtype"file"ref"uploadRef"change"upload"multiple/><templatev-for"item in fileList":key"item.key"><br><…

spring复习:(39)注解方式的ProxyFactoryBean

一、定义接口 package cn.edu.tju.study.service;public interface MyService {void myMethod(); }二、定义实现类&#xff1a; package cn.edu.tju.study.service;public class MyServiceImpl implements MyService{Overridepublic void myMethod() {System.out.println(&qu…

Redis 读写分离 使用redisTemplate执行lua脚本时,报错处理

项目框架说明 项目配置 <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.5.4</version></parent>....<dependency><groupId>org.springfra…

(学习笔记-TCP基础知识)TCP与UDP区别

UDP UDP不提供复杂的控制机制&#xff0c;利用IP提供面向[无连接]的通信服务。 UDP协议非常简单&#xff0c;头部只有8个字节(位)&#xff0c;UDP的头部格式如下&#xff1a; 目标和源端口&#xff1a;主要是告诉UDP协议应该把报文发给哪个进程包长度&#xff1a;该字段保存了…

TinyKv流程梳理三

split流程 处理协程启动 func (bs *Raftstore) startWorkers(peers []*peer) {ctx : bs.ctxworkers : bs.workersrouter : bs.routerbs.wg.Add(2) // raftWorker, storeWorkerrw : newRaftWorker(ctx, router)go rw.run(bs.closeCh, bs.wg)sw : newStoreWorker(ctx, bs.store…

基于Web API drap事件的简单拖拽功能

基于Web API drap事件的简单拖拽功能 效果示例图代码示例 效果示例图 代码示例 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title><style type"text/css">* {padding: 0px;margin: 0px;box-s…

uniapp动态获取列表中每个下标的高度赋值给另一个数组(完整代码附效果图)

uniapp实现动态获取列表中每个下标的高度&#xff0c;赋值给另一个数组。 先看效果图&#xff1a; 完整代码&#xff1a; <template><div class""><div class"">我是A列表&#xff0c;我的高度不是固定的</div><div class&qu…

MySQL 坐标批量计算及优化

文章目录 1、坐标计算2、优化 现在有一个需求&#xff0c;就是找出距离某用户最近的一些点&#xff0c;一种实现方法就是调用地图的api来计算筛选&#xff0c;另外一种就是在数据库中计算&#xff0c;考虑到地图api有并发量限制&#xff0c;所以选用数据库计算的方式。 1、坐标…

ThunderScope开源示波器

简介 4CH&#xff0c;1GSa/S 开源示波器。前端很简洁&#xff0c;BUF802LMH6518&#xff0c;ADC是HMCAD1511&#xff0c;用Xilinx A7 FPGA进行控制&#xff0c;数据通过PCIE总线传输到上位机处理。目前这个项目已经被挂到了Xilinx官网&#xff0c;强。 设计日志&#xff1a;h…

Unity自定义后处理——Vignette暗角

大家好&#xff0c;我是阿赵。   继续说一下屏幕后处理的做法&#xff0c;这一期讲的是Vignette暗角效果。 一、Vignette效果介绍 Vignette暗角的效果可以给画面提供一个氛围&#xff0c;或者模拟一些特殊的效果。 还是拿这个角色作为底图 添加了Vignette效果后&#xff0…

软件测试银行项目面试过程

今天参加了一场比较正式的面试&#xff0c;汇丰银行的视频面试。在这里把面试的流程记录一下&#xff0c;结果还不确定&#xff0c;但是面试也是自我学习和成长的过程&#xff0c;所以记录下来大家也可以互相探讨一下。 请你做一下自我介绍&#xff1f;&#xff08;汇丰要求英…

第108天:免杀对抗-Python混淆算法反序列化打包生成器Py2exeNuitka

知识点 #知识点&#xff1a; 1、Python-对执行代码做文章 2、Python-对shellcode做文章 3、Python-对代码打包器做文章#章节点&#xff1a; 编译代码面-ShellCode-混淆 编译代码面-编辑执行器-编写 编译代码面-分离加载器-编写 程序文件面-特征码定位-修改 程序文件面-加壳花指…

webpack打包之 copy-webpack-plugin

copy-webpack-plugin 打包复制文件插件。 1、什么时候要使用&#xff1f; 在离线应用中&#xff0c;前端所有文件都需在在本地&#xff0c;有些文件&#xff08;比如iconFont以及一些静态img)需要转为离线文件&#xff0c;这些文件可以直接引用更方便些&#xff0c;这就需要在打…

6.6Jmeter远程调度Linux机器Jmeter测试

1、配置Agent和启动 1.1、打开jmeter/bin目录下的jmeter.properties 1、server_port1099取消注释 2、remote_hosts127.0.0.1 改为remote_hosts127.0.0.1:1099 或者是remote_hostsAgent机的ip:1099 3、server.rmi.localport1099 4、server.rmi.ssl.disablefalse改为true&#x…

基于springboot+Redis的前后端分离项目之分布式锁(四)-【黑马点评】

&#x1f381;&#x1f381;资源文件分享 链接&#xff1a;https://pan.baidu.com/s/1189u6u4icQYHg_9_7ovWmA?pwdeh11 提取码&#xff1a;eh11 分布式锁 分布式锁1 、基本原理和实现方式对比2 、Redis分布式锁的实现核心思路3 、实现分布式锁版本一4 、Redis分布式锁误删情况…