C++ list介绍(迭代器失效)
一、常用接口
reverse逆置
sort排序(默认升序)
仿函数greater<int>
merge合并,可以全部合并,也可以一部分合并
unique:去重(先排序,再去重)
remove:删除e值,而不是erase的pos位置删
splice(粘接):其实就是transform(转移)
把某个位置i,转移到当前链表的某个position之前
list的sort效率很低,底层用归并,但是数据访问很低,因为是链表
vectror的sort更好一些,因为是连续的空间
二、迭代器封装问题
原生指针就是天生的迭代器(但是前提是物理空间是连续的)
为什么??
因为解引用就是该数据,++就是下一个数据
但是如果空间是不连续的就会出问题
例如list的原生指针就不能作为迭代器,因为不符合预期
因为list的原生指针式list*,但是list*++是错误的,因为不是连续的空间
解引用list*++,就是在原来地址位置,向后移动一个list类型大小的距离,指向该位置
但是因为不是连续的空间,所以,移动后的list*并不是下一个节点的地址
那怎么办呢?
改造一下
我们用一个类去封装这个原生指针,然后用这个类去控制这个原生指针
重载++list为移动到下一个节点的位置
需要处理的是这个部分
用类封装一个原生指针,这个原生指针也是一个模板
然后重定义这个原生指针为iterator
也就是说,这个itrator就是一个原生指针
这个原生指针也是一个模板
那么,当我们传入任意类型时,原生指针模板就会自动推导出其对应的指针
只是这个指针取了一个别名,叫做iterator,即迭代器
这就充分利用了类型的内涵
也就是说此处的迭代器底层还是一个指针
但是这个指针的行为不符合我们的预期
所以我们需要封装,重载行为
指针是内置类型
前置++和后置++是如何判断的呢?
因为函数重载只看参数列表,返回值不影响
所以,在后置++的重载参数列表加一个占位参数,int
这样就会区别两个函数
迭代器比较:就是比较指针,指针就是地址。地址相等,迭代器相等,否则不等
iterator的特点是不管底层是什么
三、list模拟实现(原码)
insert:
参数为iterator
找到当前的节点
记录前,后,插入即可
erase:参数pos也是iterator指针
删除节点后,当前节点的指针1iterator失效
所以要更新iterator
返回下一个节点的指针
pop_back:删除--end()
end是head,是头节点
resize:尾删和尾插
#pragma once
#include<assert.h>
#include<iostream>
using namespace std;namespace myspace
{//节点template <class T>struct list_node{list_node(const T& val = 0):_date(val), _next(nullptr), _prev(nullptr){}list_node<T>* _next;list_node<T>* _prev;T _date;};//迭代器template <class T, class Ref, class ptr>struct list_iterator{typedef list_node<T> node;typedef list_iterator<T, Ref, ptr> self;//模板推导list_iterator(node* node):_node(node){}//++itself& operator++(){_node = _node->_next;return *this;}//it++self& operator++(int){self tmp = _node;_node = _node->_next;return tmp;}//--itself& operator--(){_node = _node->_prev;return *this;}//it--self& operator--(int){self tmp = _node;_node = _node->_prev;return tmp;}//operatorT& operator*(){return _node->_date;}T* operator->(){return &_node->_date;}bool operator==(const self& it){return _node == it._node;}bool operator!=(const self& it){return _node != it._node;}node* _node;};//反向迭代器template <class T, class Ref, class ptr>struct list_reverse_iterator{typedef list_node<T> node;typedef list_reverse_iterator<T, Ref, ptr> self;//模板推导list_reverse_iterator(node* node):_node(node){}//++itself& operator++(){_node = _node->_prev;return *this;}//it++self& operator++(int){self tmp = _node;_node = _node->_prev;return tmp;}//--itself& operator--(){_node = _node->_next;return *this;}//it--self& operator--(int){self tmp = _node;_node = _node->_next;return tmp;}//operatorT& operator*(){node* tmp = _node->_prev;_node = _node->_prev;return tmp->_date;}T* operator->(){node* tmp = _node->_prev;_node = _node->_prev;return &tmp->_date;}bool operator==(const self& it){return _node == it._node;}bool operator!=(const self& it){return _node != it._node;}node* _node;};//一般对象的iterator和const对象的const_iterator//由于两者对应的实现不同,因此,一般的方式是写两个类//但是,二者的区别只有*引用和->引用两者的不同//所以,如果要书写两个类,显的臃肿//所以,可以使用模板//在需要的地方使用模板推导template <class T>class list{typedef list_node<T> node;public:typedef list_iterator<T, T&, T*> iterator;//一般对象的iteratortypedef list_iterator<T, const T&, const T*> const_iterator;//const对象的iteratortypedef list_reverse_iterator<T, T&, T*> reserve_iterator;//reserve_iteratorvoid empty_init(){node* newnode = new node;newnode->_next = newnode;newnode->_prev = newnode;this->_head = newnode;_size = 0;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}bool empty(){return size() == 0;}size_t size(){return _size;}list(){empty_init();}//lt1(lt2)//需要重新搞出一个新的list//(this指针就是lt1)list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}~list(){clear();delete _head;_head = nullptr;}iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}reserve_iterator rbegin(){return _head;}reserve_iterator rend(){return _head->_next;}//void swap(const list<T>& lt)//{// std::swap(_head,lt._head);// std::swap(lt._size, _size);//}void push_back(const T& val){insert(_head, val);}void push_front(const T& val){insert(begin(), val);}void pop_back(){erase(_head->_prev);}void pop_front(){erase(begin());}void insert(iterator pos, const T& val){node* tmp = new node(val);node* next = pos._node;node* prev = pos._node->_prev;prev->_next = tmp;tmp->_prev = prev;next->_prev = tmp;tmp->_next = next;++_size;}iterator erase(iterator pos){if (_size == 0)return nullptr;node* cur = pos._node;node* next = cur->_next;node* prev = cur->_prev;next->_prev = prev;prev->_next = next;delete cur;pos = nullptr;--_size;return next;}private:node* _head;size_t _size;};//打印const对象void print(const list<int>& clt){list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";}cout << endl;}//正常的增删改void test1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.insert(lt.begin(), 10);//lt.erase(lt.begin());lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_back();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test2(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.clear();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << lt.size() << endl;cout << endl;}void test3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;cout << lt.empty() << endl;}void test4(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);//list<int>::const_iterator it = lt.begin();//while (it != lt.end())//{// cout << *it << " ";// ++it;//}cout << endl;cout << lt.empty() << endl;}void print_list(const list<int>& clt){//const对象的迭代器//const_iterator迭代器是一个单独的对象//为了区别于一般对象,单独搞了一个const_iterator类//这个const_iterator类的目的在于,可以正常进行遍历,但是不能对内部的内容进行修改//因为实现方法不同,一个类无法实现,因此可以考虑使用模板list<int>::const_iterator it = clt.begin();while (it != clt.end()){//*it += 10;cout << *it << " ";++it;}cout << endl;}void test5(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);lt.push_front(100);//lt.erase(lt.end());lt.pop_back();lt.pop_back();lt.pop_back();lt.pop_front();list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;}void test6(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt2(lt);list<int> lt3;lt3.push_back(10);lt3.push_back(11);lt3.push_back(12);lt3.push_back(13);//lt3.swap(lt2);list<int>::iterator it = lt3.begin();while (it != lt3.end()){cout << *it << " ";++it;}cout << endl;}void test7(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::reserve_iterator rlt = lt.rbegin();while (rlt != lt.rend()){cout << *rlt << " ";}cout << endl;}}
四、相关细节
什么时候用struct,什么时候用class?
数据都是共有的时候就可以使用struct
模拟实现的时候,需要定义一个自己的命名空间,防止和库内冲突
将指针类型设置为模板,因为要支持不同数据的list
typedef ListNode<T> node #意为将节点设置为模板
但是,为了便于理解,我们编写代码的时候,还是使用node,便于理解
但是实际上,这个node其实是一个模板,我们用了一个typedef宏替换实现的
创建一个新节点的时候,也是,直接new node
这样就会直接开辟一个新空间节点出来,一个模板类型的空间节点
模板的理解:很简单
就是多了一个template<class T>而已
然后将对应的东西设置为T,再typedef就是了
例如:我要将list的节点设置为模板,那么:
typedef ListNode<T> node
节点设置为模板:ListNode<T>
换名字:typedef ListNode<T> node
不要把模板看的这么复杂
也不要吧typedef看的太复杂
list中at会检查越界,[]不会检查
相关文章:

C++ list介绍(迭代器失效)
一、常用接口 reverse逆置 sort排序(默认升序) 仿函数greater<int> merge合并,可以全部合并,也可以一部分合并 unique:去重(先排序,再去重) remove:删除e值&#…...
codeforces 1809C
很巧妙的构造 题目链接 题目大意 要求构造长度为 n n n的数组满足以下条件 任意 i i i, − 1000 < a [ i ] < 1000 -1000<a[i]<1000 −1000<a[i]<1000有 k k k个和为正数的子串其余子串和为负数 思路 我们发现与子数组内元素的和有关&…...

Nginx part3 创建一个https的网站
目录 HTTPS 公钥和密钥 加密解密方式: https搭建步骤 强调一下 1、准备环境 2、配置文件 3、制作证书 4、进行设置 HTTPS 啥是https,根据百度:HTTPS (全称:Hypertext Transfer Protocol Secure)&a…...

事件高级。
一、注册事件(绑定事件) 就是给元素添加事件 注册事件有两种方式:传统方式和方法监听注册方式 1 传统注册方式 方法监听注册事件 2、 addEventListener 事件监听方式 里面的事件类型是字符串,必定加引号,而且不带o…...

Vue从入门到实战Day04
一、组件的三大组成部分(结构/样式/逻辑) 1. scoped样式冲突 默认情况:写在组件中的样式会全局生效 -> 因此很容易造成多个组件之间的样式冲突问题。 1. 全局样式:默认组件中的样式会作用到全局 2. 局部样式:可以…...

Linux学习笔记:信号
信号 在Linux中什么是信号信号的产生方式硬件产生的信号软件产生的信号异常产生的信号 进程对信号的处理信号的保存信号方法更改函数signal信号处理的更改恢复默认信号忽略 信号的管理信号集 sigset_t对信号集的操作 信号的捕捉过程 在Linux中什么是信号 在 Linux 系统中&…...
C#中的隐式类型转换和显式类型转换
在C#中,类型转换分为隐式类型转换(Implicit Type Conversion)和显式类型转换(Explicit Type Conversion),也称为隐式转换和强制转换。 隐式类型转换(Implicit Type Conversion) 隐…...
linux上如何排查JVM内存过高?
在Linux上排查JVM内存过高的问题,可以采用以下几种方法: 1. **使用top命令查看进程**:通过top命令可以观察到系统中资源占用情况,包括CPU和内存。当收到内存过高的报警时,可以使用top命令来查看是哪个进程的内存使用率…...

第四届上海理工大学程序设计全国挑战赛 J.上学 题解 DFS 容斥
上学 题目描述 usst 小学里有 n 名学生,他们分别居住在 n 个地点,第 i 名学生居住在第 i 个地点,这些地点由 n−1 条双向道路连接,保证任意两个地点之间可以通过若干条双向道路抵达。学校则位于另外的第 0 个地点,第…...

word-排版文本基本格式
1、文本的基本格式:字体格式、段落格式 2、段落:word排版的基本控制单位 3、每敲一次回车,为一个段落标记,注意区分换行符和段落标记,换行符为指向下的箭头,段落标记为带拐弯的箭头,换行符&…...
目标检测YOLO实战应用案例100讲-无监督领域自适应目标检测方法研究与应用(五)
目录 多源无监督领域自适应目标检测方法 4.1研究现状及问题形成 4.2相关工作详述...

通过python实现Google的精准搜索
问题背景: 我想通过Google或者其他网站通过精准搜索确认该产品是否存在,但是即使该产品不存在Google也会返回一些相关的url链接,现在想通过python实现搜索结果的精准匹配以确认该产品是否为正确的名称【可以通过google搜索到,如果…...

Nios-II编程入门实验
文章目录 一 Verilog实现流水灯二 Nios实现流水灯2.1 创建项目2.2 SOPC添加模块2.3 SOPC输入输出连接2.4 Generate2.5 软件部分2.6 运行结果 三 Verilog实现串口3.1 代码3.2 引脚3.3 效果 四 Nios2实现串口4.1 sopc硬件设计4.2 top文件4.3 软件代码4.4 实现效果 五 参考资料六 …...

从0开始学python(七)
目录 前言 1 break、continue和pass函数 1.1 break 1.2 continue 1.3 pass 2、序列的索引及切片操作 2.1字符串的索引和切片 2.1.1 字符串索引 2.1.2 字符串切片 总结 前言 上一篇文章我们介绍了python中的循环结构,包括for和while的使用。本章接着往下讲。…...
【二叉树算法题记录】404. 左叶子之和
题目描述 给定二叉树的根节点 root ,返回所有左叶子之和。 题目分析 其实这题无论是迭代法还是递归法,最重要的是要明确判断左叶子的条件:当前节点有左孩子,且这个左孩子没有它的左孩子和右孩子。 迭代法 感觉只要二叉树相关…...

面试集中营—Spring篇
Spring 框架的好处 1、轻量:spring是轻量的,基本的版本大约2MB; 2、IOC:控制反转,Spring的IOC机制使得对象之间的依赖不再需要我们自己来控制了,而是由容易来控制,一个字:爽…...

Lia 原理
训练阶段 论文流程: 具体实现: 通过latent space传递运动信息,实现分两部分。 1)image space->latent space 将源图像映射到隐空间编码。X_s (source image )映射到编码Z_sr,通过W_rd方向上的变化,得到新的编码Z…...

文本批量操作技巧:内容查找不再繁琐,自动化批量移动至指定文件夹
在文本处理和信息管理的日常工作中,我们经常需要处理大量的文件和数据。面对这些海量的信息,如何快速而准确地查找特定的内容,并将它们批量移动至指定的文件夹,成为了一项关键的技能。本文将介绍办公提效工具一些实用的文本批量操…...

[数据结构]动画详解单链表
💖💖💖欢迎来到我的博客,我是anmory💖💖💖 又和大家见面了 欢迎来到动画详解数据结构系列 用通俗易懂的动画的动画使数据结构可视化 先来自我推荐一波 个人网站欢迎访问以及捐款 推荐阅读 如何低…...

图片批量管理迈入智能新时代:一键输入关键词,自动生成并保存惊艳图片,轻松开启创意之旅!
在数字化时代,图片已成为我们表达创意、记录生活、传递信息的重要工具。然而,随着图片数量的不断增加,如何高效、便捷地管理这些图片,却成为了一个令人头疼的问题。 第一步,进入首助编辑高手主页面,在上方…...

网络六边形受到攻击
大家读完觉得有帮助记得关注和点赞!!! 抽象 现代智能交通系统 (ITS) 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 (…...

python打卡day49
知识点回顾: 通道注意力模块复习空间注意力模块CBAM的定义 作业:尝试对今天的模型检查参数数目,并用tensorboard查看训练过程 import torch import torch.nn as nn# 定义通道注意力 class ChannelAttention(nn.Module):def __init__(self,…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
第25节 Node.js 断言测试
Node.js的assert模块主要用于编写程序的单元测试时使用,通过断言可以提早发现和排查出错误。 稳定性: 5 - 锁定 这个模块可用于应用的单元测试,通过 require(assert) 可以使用这个模块。 assert.fail(actual, expected, message, operator) 使用参数…...
AI编程--插件对比分析:CodeRider、GitHub Copilot及其他
AI编程插件对比分析:CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展,AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者,分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
Rapidio门铃消息FIFO溢出机制
关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系,以下是深入解析: 门铃FIFO溢出的本质 在RapidIO系统中,门铃消息FIFO是硬件控制器内部的缓冲区,用于临时存储接收到的门铃消息(Doorbell Message)。…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

有限自动机到正规文法转换器v1.0
1 项目简介 这是一个功能强大的有限自动机(Finite Automaton, FA)到正规文法(Regular Grammar)转换器,它配备了一个直观且完整的图形用户界面,使用户能够轻松地进行操作和观察。该程序基于编译原理中的经典…...