List ---- 模拟实现LIST功能的发现
目录
- list
- list概念
- list 中的迭代器
- list迭代器知识
- const迭代器写法
- list访问自定义类型
- 附录代码
list
list概念
- list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
- list的底层是双向链表结构,双向链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向
其前一个元素和后一个元素。- list与forward_list非常相似:最主要的不同在于forward_list是单链表,只能朝前迭代,已让其更简单高
效。- 与其他的序列式容器相比(array,vector,deque),list通常在任意位置进行插入、移除元素的执行效率
更好。- 与其他序列式容器相比,list和forward_list最大的缺陷是不支持任意位置的随机访问,比如:要访问list
的第6个元素,必须从已知的位置(比如头部或者尾部)迭代到该位置,在这段位置上迭代需要线性的时间
开销;list还需要一些额外的空间,以保存每个节点的相关联信息(对于存储类型较小元素的大list来说这
可能是一个重要的因素)
list 中的迭代器
有兴趣的可以直接跳转附录代码中,里面几乎涵盖了所有的问题答案.
list迭代器知识
迭代器原理就是对原生指针的封装,帮助我们更好的使用指针来对节点的内容进行访问。
迭代器目前学习的进度来看是分成普通迭代器和const迭代器。在对list的模拟实现过程中发现了许多新的迭代器知识点。
const迭代器写法
由于对迭代器封装后的代码重命名为:typedef __list_iterator<T > iterator;
所以下意识会认为const迭代器应该是这个样子的://typedef __list_const_iterator<T> const_iterator;
实际上这是有问题的!
因为const迭代器修饰的应该节点内部的数据不可以被修改,而迭代器本身是可以前后移动来遍历链表。 而
const_iterator
所表达的意思是T* const
,但是我们想要的是const T*
。 这两者的区别便是前一个T* const
可以修改节点内部数据信息,但因为不可以修改地址所以不能遍历链表,,而后一个const T*
不可以修改数据信息,但是可以遍历链表。
要想办法实现const T*
!!!!
list中的const迭代器实际上是保证对信息不可修改,所以只需要对读取信息的操作赋予控制是否为const属性的操作,即为T* operator*()
确保在某些时刻是const属性。所以可以在模板上对其进行特殊化操作: template<class T, class Ref>
template<class T, class Ref>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T, Ref> self;node* _node;__list_iterator(node* n):_node(n){}Ref operator*()//T* operator(){return _node->_data;}};template<class T>class list{typedef list_node<T> node;public:typedef __list_iterator<T, T&> iterator;//使用普通迭代器就更改Ref就好了typedef __list_iterator<T, const T&> const_iterator;
在需要const迭代器时候,传递const T&,而需要普通迭代器就直接传递T&,这样不仅解决的繁琐的复用问题,还能够满足使用。
list访问自定义类型
迭代器要么是原生指针,要么是自定义类型对原生指针的封装,在模拟指针的行为。
而访问自定义类型不能使用解引用操作,而是使用访问操作符->
,所以list库对访问自定义类型也做了对应的设置,即重载operator->
但是因为要访问自定义类型就一棒子大打死,就只能使用operator->
来进行访问,内部的函数大可以直接访问,而复用又太过于繁琐了,所以又新增了特殊的模板类,控制是访问自定义类型还是访问内部函数!
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* n):_node(n){}Ptr operator->(){return &_node->_data;}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;}
struct AA{int _a1;int _a2;AA(int a1 = 0, int a2 = 0)//全缺省的默认构造:_a1(a1), _a2(a2){}};void test_list2(){list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));list<AA>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << "," << it->_a2 << " ";++it;}cout << endl;}
因此不仅仅可以访问是否为const属性的信息,还可以控制访问是否为自定义类型的参数
附录代码
#pragma once#include<iostream>
#include<assert.h>
using namespace std;
namespace lby
{template<class T>struct list_node //节点的类//struct默认为公有,不打算对内容进行限制就用struct{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x = T()):_next(nullptr), _prev(nullptr), _data(x){}};//迭代器要么是原生指针,要么是自定义类型对原生指针的封装,在模拟指针的行为template<class T, class Ref, class Ptr>//使用普通迭代器就更改Ref就好了struct __list_iterator//封装的是迭代器,而迭代器的本质是用一个类去封装这个 node*,即指针指向这个链表的头节点{typedef list_node<T> node;typedef __list_iterator<T, Ref, Ptr> self;node* _node;//注意节点的指针不属于迭代器,只是让迭代器封装之后的一系列操作,不支持释放,释放是链表的事情,迭代器只能使用节点,不能释放节点__list_iterator(node* n):_node(n){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}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;}self operator--(int){self tmp(this);_node = _node->_prev;return tmp;}bool operator!=(const self& x)//传递的是迭代器中的x{return _node != x._node;}bool operator==(const self& x){return _node == x._node;}};/*template<class T>struct __list_const_iterator//封装的是迭代器,而迭代器的本质是用一个类去封装这个 node*,即指针指向这个链表的头节点{typedef list_node<T> node;typedef __list_const_iterator<T> self;node* _node;//注意节点的指针不属于迭代器,只是让迭代器封装之后的一系列操作,不支持释放,释放是链表的事情,迭代器只能使用节点,不能释放节点__list_const_iterator(node* n):_node(n){}const T& operator*()//控制整个返回值不可修改{return _node->_data;}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;}self operator--(int){self tmp(this);_node = _node->_prev;return tmp;}bool operator!=(const self& x)//传递的是迭代器中的x{return _node != x._node;}bool operator==(const self& x){return _node == x._node;}};*/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 __list_const_iterator<T> const_iterator;//typedef const iterator const_itrator;//绝对不可以,这种方式const修饰的是地址 --> T* const,而不是const T*;iterator begin(){return iterator(_head->_next);}iterator end(){return iterator(_head);}//iterator begin() const//这里有个问题,既然是const指针,那就应该是常量,但是为什么你还能改变指向的位置?//{// return iterator(_head->_next);//因为const指针修饰的是*this,即this指针指向的内容,它指向的内容是_head这个的指针,// //即为修饰的是_head这个指针本身!也就是说_head本身不能被改变,它指向的内容是可以改变的// //但是与我们的预期不符,因为const迭代器他是只读操作,不允许改变内容,如果他内容是可以改变的,我为什么要使用const迭代器呢const迭代器与普通迭代器区别是:const迭代器本身是可以修改的(可以前后移动去访问),但是const迭代器指向的内容是不可以修改的--> 要const T*,不要T* const !//}//iterator end() const//{// return iterator(_head); //}const_iterator begin() const{return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}list(){empty_init();}template<class Iterator>list(Iterator first, Iterator last){empty_init();//先构造头节点while (first != last){push_back(*first);//push_back 的前提是有哨兵位的头节点,所以需要先构造头节点++first;}}void swap(list<T>& t){std::swap(_head, t._head);}list(const list<T>& lt)//lt2(lt1){/*empty_init();//正常写法for (auto e : lt){push_back(e);}*/empty_init();list<T> tmp(lt.begin(), lt.end());//借助模板类进行复制后交换swap(tmp);}//lt1 = lt3 list<T>& operator=(list<T> lt)//(list<T>& lt)不能引用传引用,因为会将原来的lt进行修改{swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){it = erase(it);//erase(it++);//这个地方析构的值是返回的迭代器对象,是it的拷贝,不是it}}void push_back(const T& x){//node* tail = _head->_prev;//node* new_node = new node(x);//需要node(list_node<T>)的构造函数//tail->_next = new_node;//new_node->_prev = tail;//new_node->_next = _head;//_head->_prev = new_node;insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void insert(iterator pos, const T& x)//链表的迭代器插入数据不会失效,因为pos指针指向的位置是不变的{node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}void pop_back(){erase(--end());}void pop_front(){erase(begin());}iterator erase(iterator pos)//由于pos指针位置被析构了,所以迭代器失效了{assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);}private:node* _head;};void print_list(const list<int>& lt){list<int>::const_iterator it = lt.begin();while (it != lt.end()){//(*it) *= 2;//const不可修改cout << *it << " ";++it;}cout << endl;}void test_list1(){const list<int> l;//const对象在定义时,最开始不会赋给常值,因为要初始化,否则没办法进行初始化,之后才会赋给const属性//const对象在定义的一瞬间不会给const属性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;for (auto p : lt){cout << p << " ";}cout << endl;print_list(lt);}struct AA{int _a1;int _a2;AA(int a1 = 0, int a2 = 0)//全缺省的默认构造:_a1(a1), _a2(a2){}};void test_list2(){list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));list<AA>::iterator it = lt.begin();while (it != lt.end()){//cout << (*it)._a1 << " " << (*it)._a2 << " ";cout << it->_a1 << "," << it->_a2 << " ";//由于函数重载了 -> ,所以本来应该是it->->_a1,编译器优化了设置,变成了it->_a1;//it.operator->()->_a1,it.operator->()返回的是T*,T*->_a1就可以访问++it;}cout << endl;}void test_list3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto p : lt){cout << p << " ";}cout << endl;auto pos = lt.begin();++pos;lt.insert(pos, 100);for (auto p : lt){cout << p << " ";}cout << endl;lt.push_front(200);lt.push_front(300);for (auto p : lt){cout << p << " ";}cout << endl;lt.push_back(400);lt.push_back(500);for (auto p : lt){cout << p << " ";}cout << endl;lt.pop_back();lt.pop_front();for (auto p : lt){cout << p << " ";}cout << endl;lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();for (auto p : lt){cout << p << " ";}cout << endl;}void test_list4(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto p : lt){cout << p << " ";}cout << endl;lt.clear();for (auto p : lt){cout << p << " ";}cout << endl;lt.push_back(10);lt.push_back(2);lt.push_back(30);lt.push_back(1);for (auto p : lt){cout << p << " ";}cout << endl;}void test_list5(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto p : lt){cout << p << " ";}cout << endl;list<int> lt2(lt);for (auto e : lt2){cout << e << " ";}cout << endl;}
}
相关文章:

List ---- 模拟实现LIST功能的发现
目录 listlist概念 list 中的迭代器list迭代器知识const迭代器写法list访问自定义类型 附录代码 list list概念 list是可以在常数范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。list的底层是双向链表结构,双向链表中每个元素…...
HashMap和HashTable区别问题
并发:hashMap线程不安全,hashTable线程安全,底层在put操作的方法上加了synchronized 初始化:hashTable初始容量为11,hashmap初始容量为16 阔容因子:阔容因子都是0.75 扩容比例: 补充 hashMap…...

mysql -> 达梦数据迁移(mbp大小写问题兼容)
安装 注意后面初始化需要忽略大小写 初始化程序启动路径 F:\dmdbms\tool dbca.exe 创建表空间,用户,模式 管理工具启动路径 F:\dmdbms\tool manager.exe 创建表空间 创建用户 创建同名模式,指定模式拥有者TEST dts 工具数据迁移 mysql -&g…...
leetcode热门100题1-4
第一天 两数之和 //暴力枚举 class Solution { public:vector<int> twoSum(vector<int>& nums, int target) {int n nums.size();for (int i 0; i < n; i) {for (int j i 1; j < n; j) {if (nums[i] nums[j] target) {return {i, j};}}}return {…...

作业:IO:day2
题目一 第一步:创建一个 struct Student 类型的数组 arr[3],初始化该数组中3个学生的属性 第二步:编写一个叫做save的函数,功能为 将数组arr中的3个学生的所有信息,保存到文件中去,使用fread实现fwrite 第三步…...

UVM: TLM机制
topic overview 不建议的方法:假如没有TLM TLM TLM 1.0 整个TLM机制下,底层逻辑离不开动作发起者和被动接受者这个底层的模型基础,但实际上,在验证环境中,任何一个组件,都有可能成为动作的发起者࿰…...

flink的EventTime和Watermark
时间机制 Flink中的时间机制主要用在判断是否触发时间窗口window的计算。 在Flink中有三种时间概念:ProcessTime、IngestionTime、EventTime。 ProcessTime:是在数据抵达算子产生的时间(Flink默认使用ProcessTime) IngestionT…...

arcgis的合并、相交、融合、裁剪、联合、标识操作的区别和使用
1、相交 需要输入两个面要素,最终得到的是两个输入面要素相交部分的结果面要素。 2、合并 合并能将两个单独存放的两个要素类的内容,汇集到一个要素类里面。 3、融合 融合能将一个要素类内的所有元素融合成一个整体。 4、裁剪 裁剪需要输入两个面要…...
【Leetcode 热题 100】20. 有效的括号
问题背景 给定一个只包括 ‘(’,‘)’,‘{’,‘}’,‘[’,‘]’ 的字符串 s s s,判断字符串是否有效。 有效字符串需满足: 左括号必须用相同类型的右括号闭合。左括号必须以正确的顺序闭合。每…...
比较procfs 、 sysctl和Netlink
procfs 文件系统和 sysctl 的使用: procfs 文件系统(/proc) procfs 文件系统是 Linux 内核向用户空间暴露内核数据结构以及配置信息的一种方式。`procfs` 的挂载点是 /proc 目录,这个目录中的文件和目录呈现内核的运行状况和配置信息。通过读写这些文件,可以查看和控制内…...
Leetcode 3413. Maximum Coins From K Consecutive Bags
Leetcode 3413. Maximum Coins From K Consecutive Bags 1. 解题思路2. 代码实现 题目链接:3413. Maximum Coins From K Consecutive Bags 1. 解题思路 这一题的话思路上整体上就是一个遍历,显然,要获得最大的coin,其选取的范围…...
MakeFile使用指南
文章目录 1. MakeFile 的作用2. 背景知识说明2.1 程序的编译与链接2.2 常见代码的文档结构 3. MakeFile 的内容4. Makefile的基本语法5. 变量定义5.1 一般变量赋值语法5.2 自动化变量 6. 通配符 参考: Makefile教程:Makefile文件编写1天入门 Makefile由浅…...

矩阵碰一碰发视频的视频剪辑功能源码搭建,支持OEM
在短视频创作与传播领域,矩阵碰一碰发视频结合视频剪辑功能,为用户带来了高效且富有创意的内容产出方式。这一功能允许用户通过碰一碰 NFC 设备触发视频分享,并在分享前对视频进行个性化剪辑。以下将详细阐述该功能的源码搭建过程。 一、技术…...
VB.NET CRC32 校验
在 VB.NET 中实现 CRC32 校验并在校验失败时退出程序,你可以按照以下步骤进行: 实现 CRC32 计算函数:首先,你需要一个函数来计算给定数据的 CRC32 值。 比较计算的 CRC32 值:然后,你需要将计算出的…...

冒充者综合征上线了
背景 今天干了一件蠢事儿,上周末咸鱼上有人拍了之前发布的一个java程序,基于 JWT 实现的一个五子棋游戏的源代码。想着反正又没事,就找到了移动硬盘拷贝出那个源代码上传网盘发货了。 今天买家找我说解压不了,我电脑解压正常。就…...

【大模型】百度千帆大模型对接LangChain使用详解
目录 一、前言 二、LangChain架构与核心组件 2.1 LangChain 核心架构 2.2 LangChain 核心组件 三、环境准备 3.1 前置准备 3.1.1 创建应用并获取apikey 3.1.2 开通付费功能 3.2 获取LangChain文档 3.3 安装LangChain依赖包 四、百度千帆大模型对接 LangChain 4.1 LL…...
Redis相关面试
以下是一些在面试中关于 Redis 最常被问到的问题,涵盖了 Redis 的基础概念、数据结构、持久化、主从复制、哨兵、集群、应用场景以及常见的缓存问题等。可以根据自身实际项目经验,结合下面的要点进行深入讲解。 1. Redis 基础与特点 Redis 是什么&#x…...

使用强化学习训练神经网络玩俄罗斯方块
一、说明 在 2024 年暑假假期期间,Tim学习并应用了Q-Learning (一种强化学习形式)来训练神经网络玩简化版的俄罗斯方块游戏。在本文中,我将详细介绍我是如何做到这一点的。我希望这对任何有兴趣将强化学习应用于新领域的人有所帮助…...

java中的日期处理:只显示日期,不显示时间的两种处理方式
需要记录某个操作的操作时间,数据库中该字段为DATE类型; 插入数据的时候,使用数据库函数NOW()获取当前日期并插入: <insert id"batchInsertOrgTestersByProjectId">insert into project_org_testers(project_un…...
腾讯云AI代码助手编程挑战赛——贪吃蛇小游戏
作品介绍 贪吃蛇小游戏需要控制蛇的移动方向,使其吃掉地图上随机出现的食物,每吃掉一个食物,蛇的身体就会增长一格,是一款老少皆宜的小游戏,我们可以用腾讯ai助手生成全部代码,简单方便快捷。 技术架构 …...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
脑机新手指南(八):OpenBCI_GUI:从环境搭建到数据可视化(下)
一、数据处理与分析实战 (一)实时滤波与参数调整 基础滤波操作 60Hz 工频滤波:勾选界面右侧 “60Hz” 复选框,可有效抑制电网干扰(适用于北美地区,欧洲用户可调整为 50Hz)。 平滑处理&…...

【开发技术】.Net使用FFmpeg视频特定帧上绘制内容
目录 一、目的 二、解决方案 2.1 什么是FFmpeg 2.2 FFmpeg主要功能 2.3 使用Xabe.FFmpeg调用FFmpeg功能 2.4 使用 FFmpeg 的 drawbox 滤镜来绘制 ROI 三、总结 一、目的 当前市场上有很多目标检测智能识别的相关算法,当前调用一个医疗行业的AI识别算法后返回…...

springboot整合VUE之在线教育管理系统简介
可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生,小白用户,想学习知识的 有点基础,想要通过项…...

MySQL 知识小结(一)
一、my.cnf配置详解 我们知道安装MySQL有两种方式来安装咱们的MySQL数据库,分别是二进制安装编译数据库或者使用三方yum来进行安装,第三方yum的安装相对于二进制压缩包的安装更快捷,但是文件存放起来数据比较冗余,用二进制能够更好管理咱们M…...
【学习笔记】erase 删除顺序迭代器后迭代器失效的解决方案
目录 使用 erase 返回值继续迭代使用索引进行遍历 我们知道类似 vector 的顺序迭代器被删除后,迭代器会失效,因为顺序迭代器在内存中是连续存储的,元素删除后,后续元素会前移。 但一些场景中,我们又需要在执行删除操作…...
Linux系统部署KES
1、安装准备 1.版本说明V008R006C009B0014 V008:是version产品的大版本。 R006:是release产品特性版本。 C009:是通用版 B0014:是build开发过程中的构建版本2.硬件要求 #安全版和企业版 内存:1GB 以上 硬盘…...

【Linux】自动化构建-Make/Makefile
前言 上文我们讲到了Linux中的编译器gcc/g 【Linux】编译器gcc/g及其库的详细介绍-CSDN博客 本来我们将一个对于编译来说很重要的工具:make/makfile 1.背景 在一个工程中源文件不计其数,其按类型、功能、模块分别放在若干个目录中,mak…...

篇章二 论坛系统——系统设计
目录 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 1. 数据库设计 1.1 数据库名: forum db 1.2 表的设计 1.3 编写SQL 2.系统设计 2.1 技术选型 2.2 设计数据库结构 2.2.1 数据库实体 通过需求分析获得概念类并结合业务实现过程中的技术需要&#x…...
如何通过git命令查看项目连接的仓库地址?
要通过 Git 命令查看项目连接的仓库地址,您可以使用以下几种方法: 1. 查看所有远程仓库地址 使用 git remote -v 命令,它会显示项目中配置的所有远程仓库及其对应的 URL: git remote -v输出示例: origin https://…...