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助手生成全部代码,简单方便快捷。 技术架构 …...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
连锁超市冷库节能解决方案:如何实现超市降本增效
在连锁超市冷库运营中,高能耗、设备损耗快、人工管理低效等问题长期困扰企业。御控冷库节能解决方案通过智能控制化霜、按需化霜、实时监控、故障诊断、自动预警、远程控制开关六大核心技术,实现年省电费15%-60%,且不改动原有装备、安装快捷、…...

STM32F4基本定时器使用和原理详解
STM32F4基本定时器使用和原理详解 前言如何确定定时器挂载在哪条时钟线上配置及使用方法参数配置PrescalerCounter ModeCounter Periodauto-reload preloadTrigger Event Selection 中断配置生成的代码及使用方法初始化代码基本定时器触发DCA或者ADC的代码讲解中断代码定时启动…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...

零基础设计模式——行为型模式 - 责任链模式
第四部分:行为型模式 - 责任链模式 (Chain of Responsibility Pattern) 欢迎来到行为型模式的学习!行为型模式关注对象之间的职责分配、算法封装和对象间的交互。我们将学习的第一个行为型模式是责任链模式。 核心思想:使多个对象都有机会处…...
今日科技热点速览
🔥 今日科技热点速览 🎮 任天堂Switch 2 正式发售 任天堂新一代游戏主机 Switch 2 今日正式上线发售,主打更强图形性能与沉浸式体验,支持多模态交互,受到全球玩家热捧 。 🤖 人工智能持续突破 DeepSeek-R1&…...
【C++从零实现Json-Rpc框架】第六弹 —— 服务端模块划分
一、项目背景回顾 前五弹完成了Json-Rpc协议解析、请求处理、客户端调用等基础模块搭建。 本弹重点聚焦于服务端的模块划分与架构设计,提升代码结构的可维护性与扩展性。 二、服务端模块设计目标 高内聚低耦合:各模块职责清晰,便于独立开发…...

10-Oracle 23 ai Vector Search 概述和参数
一、Oracle AI Vector Search 概述 企业和个人都在尝试各种AI,使用客户端或是内部自己搭建集成大模型的终端,加速与大型语言模型(LLM)的结合,同时使用检索增强生成(Retrieval Augmented Generation &#…...
智能AI电话机器人系统的识别能力现状与发展水平
一、引言 随着人工智能技术的飞速发展,AI电话机器人系统已经从简单的自动应答工具演变为具备复杂交互能力的智能助手。这类系统结合了语音识别、自然语言处理、情感计算和机器学习等多项前沿技术,在客户服务、营销推广、信息查询等领域发挥着越来越重要…...

【C++特殊工具与技术】优化内存分配(一):C++中的内存分配
目录 一、C 内存的基本概念 1.1 内存的物理与逻辑结构 1.2 C 程序的内存区域划分 二、栈内存分配 2.1 栈内存的特点 2.2 栈内存分配示例 三、堆内存分配 3.1 new和delete操作符 4.2 内存泄漏与悬空指针问题 4.3 new和delete的重载 四、智能指针…...