vector模拟实现迭代器失效

news2024/11/15 8:15:56

目录

1.vector和vector>

1.1两者的区别

1.2遍历的方法

2.vector模拟实现的准备

3.reserve出现的问题及解决方案

4.遍历vector的三种方式

5.关于typename的使用

6.insert导致的迭代其实失效问题

6.1因为扩容导致的迭代器失效

6.2因为插入数据倒置的迭代器失效


1.vector<int>和vector<vector<int>>

1.1两者的区别

vector<int>表示的就是一个一维数组,这个一维数组的数据类型都是int类型的;

vector<vector<int>>表示的就是一个二维数组,这个数组的每一个元素都是vector<int>类型的,也就是说这个数组里面的每一个元素都是一个vector<int>的一维数组

1.2遍历的方法

我们下面这个代码里面还对于这个二维数组进行遍历,这个遍历我们使用了两个for循环;

其中这个里面包括了我们对于数组元素的修改,我们的修改提供了两个方式,第一个就是使用的这个[][]即方括号索引,我们也可以使用这个operator[],第一个是属于vector<int>的,第二个是属于int,两个方括号不是一个类里面的,两个方法是等效的;

2.vector模拟实现的准备

我们首先要构建出来一个基本的框架,方便我们进行后续的操作:其中这个里面的_finish就是到我们的这个真实数据的最后一个位置,类似于我们之前介绍的这个size,这个end_of_storage类似于我们之前介绍的这个capacity,就是这个空间大小,容量;

#pragma once
#include<assert.h>
#include<iostream>
using namespace std;


namespace bite
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;

		void reserve(size_t n)
		{
			T* temp = new T[n];
			memcpy(temp, _start, size());
			delete[] _start;

			_start = temp;
			_finish = _start + size();
			_end_of_storage = _start + n;

		}

		size_t size()
		{
			return _finish - _start;
		}

		size_t capacity()
		{
			return _end_of_storage - _start;
		}

		void push_back(const T& x)
		{
			//扩容判断语句
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			++_finish;
		}

		T& operator[](size_t n)
		{
			assert(n < size());
			return _start[n];
		}

	private:
		iterator _start = nullptr;
		iterator _finish = nullptr;
		iterator _end_of_storage = nullptr;
	};

	void test01()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);

		for (size_t i = 0; i < v.size(); i++)
		{
			cout << v[i] << " ";
		}
		cout << endl;
	}
}

3.reserve出现的问题及解决方案

我们在实现这个vector扩容的时候,这个里面会出现类似于下面的问题,就是我们的这个vector扩容是因为这个空间不够了,我们需要开辟新的空间,vector的底层使用的是内存池,但是我们这里直接使用这个new进行动态的开辟,因为即使是内存池,这个空间也是new出来的;

空间开辟完成之后,我们直接进行这个memcpy即进行拷贝,从_start开始,拷贝的数量就是这个size()空间大小,拷贝到这个temo位置上面,然后我们释放掉原来的空间;

我们让这个temp只想我们的_start,finish就是开始加上这个size()大小,capacity就是开始加上新开辟的空间n的大小,这个看似没有问题,实际上运行的时候就会报错,聪明的你发现问题了吗?

实际上,上面的这个问题就是因为这个_finish计算的时候需要去进行这个size()函数的调用,但是调用这个函数的时候,需要使用到这个finish-start,然而这个原意是让这个初始情况下的finish-start,但是这个实际上我们的finish调用的上一步,start已经被重新赋值,这个时候就违背了我们的意愿要求;

这个时候,有两个解决方案,就是先更新这个finish,再对于这个start进行更新在,这个是可以的,但是这个方式我们不推荐,因为看着很别扭,都知道这个start,finish,storage,这个时候你的顺序不对看着就难受;

这个时候我们可以使用下面的方法,就是提前记录下来这个size的空间大小,然后进行使用:

4.遍历vector的三种方式

第一种就是用这个普通的for循环进行遍历,使用下标进行容器元素的遍历;

第二个方式就是使用这个迭代器的方式,这个里面的这个vector<int>::iterator表示的实际含义就是这个iterator是一个迭代器,这个迭代器是用来对于这个vector<int>容器里面的元素进行遍历的;

第三个就是使用范围for进行遍历,使用这个auto进行这个类型的识别即可;

5.关于typename的使用

我们这个vector的使用里面是如何引入我们的这个typename的呢,首先是这个我们想要让这个vector容器里面存储这个double类型的数据,同样是对于数据的打印,我们想要这个函数打印这个double类型的数据,这个时候的我们的这个print_vector这个函数里面的参数的数据类型就是这个const vector<T>这个时候,参数会根据我们的传参进行判断,例如我们传递这个v就是vector<int>类型,这个时候的T就是int,但是当我们传递这个vector<double>的时候,这个T就是double类型了,这个就是模版;

因为这个时候函数的参数里面是这个const类型的数据,因此这个迭代器需要更改为这个const_iterator,这个要求我们需要实现const版本的函数:

这个时候就有了需要typename的地方,就是因为这个时候的vector<T>没有进行实例化,编译器无法区分这个const_iterator是静态成员变量还是迭代器,不会进入这个没有实例化的模版里面取东西,因此我们需要加上这个typename关键字;

这个vector<T>就是没有实例化,像这种vector<int>明确的给出来这个容器里面的数据类型的,就是已经进行了实例化,这个就是有无实例化的说明;

当然这个时候,貌似这个it的类型的名字很长,这个时候才是我们的auto真正的进行大展身手的时候,我们前面见到的这个aotu替换数据类型效果都不是很显著,但是这个地方的替换就很明显,凸显了auto的默认类型识别的功能;

6.insert导致的迭代其实失效问题

6.1因为扩容导致的迭代器失效

下面的这个我们通过调试就会发现,当这个需要进行扩容的时候,这个时候pos的位置应该已经变了,但是我们的这个程序里面的这个pos依然是指向的原来的空间,这个时候就是迭代器失效了,观察到的现象就是我们进行调试的时候,当这个end=pos之后,继续进行下去,按理说这个循环应该停止,但是这个时候因为我们的pos执行原来的空间,因此这个时候的循环会继续进行下去;

我们针对于上面的情况,解决方案就是我们的这个扩容的时候记录下来这个pos相对于start的相对位置,然后reserve之后对于这个pos新的位置进行更新:

6.2因为插入数据倒置的迭代器失效

我们想在第二个位置插入数据20,插入之后对于这个位置的数据进行*10的操作,但是我们运行之后发现这个未知的数据并没有按照我们的要求乘上10,这个也是一个迭代器的失效问题;

这个时候我们的做法就是把这个更新的位置记录下来,然后按照我们的需求对于这个pos的下一位置进行*10操作;

这个时候想要记录这个pos,我们就需要调用这个insert之后有返回值,这个时候我们需要对于这个insert进行改写,增加返回值:

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

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

相关文章

【精彩回顾·成都】成都 UG 生成式 AI 工作坊:AI 革命下的商业模式创新!

文章目录 前言一、活动介绍二、精彩分享内容及活动议程2.1、亚马逊云科技社区情况和活动介绍2.2、《浅谈 AIGC 商业化》2.3、《AI 浪潮下的产品落地》2.4、现场互动情况2.5、休息茶歇时间2.6、《AI 赋能商业革新&#xff1a;智能化转型的策略与实践》2.7、《动手实践&#xff1…

GAMES104:10+11游戏引擎中物理系统的基础理论算法和高级应用-学习笔记

文章目录 概览一&#xff0c;物理对象与形状1.1 对象 Actor1.2 对象形状Actor Shape 二&#xff0c;力与运动2.1 牛顿定律2.2 欧拉法2.2.1 显式欧拉法Explicit (Forward) Euler’s Method2.2.2 隐式欧拉法 Implicit (Backward) Euler’s Method2.2.3 半隐式欧拉法 Semi-implici…

【iOS】通过第三方库Masonry实现自动布局

目录 前言 约束 添加约束的规则 使用Masonry自动布局 Masonry的常见使用方法 补充 前言 在暑期完成项目时&#xff0c;经常要花很多时间在调试各种控件的位置上&#xff0c;因为每一个控件的位置都需要手动去计算&#xff0c;在遇到循环布局的控件时&#xff0c;还需要设…

使用文件系统管理硬件设备

1、描述一个文件系统 介绍如何使用文件系统来管理计算机系统中所有的硬件设计和磁盘 之前直接在sys_open/sys_read/sys_read中对具体的tty设备的打开、读写进行操作&#xff0c;并且只是非常粗浅地介绍了文件系统的一些概念。接下来将正式引入文件系统 由于文件系统需要考虑不…

从FasterTransformer源码解读开始了解大模型(2.4)代码通读05

从FasterTransformer源码解读开始了解大模型&#xff08;2.4&#xff09;代码解读05-ContextDecoder的前向01 写在前面的话 ContextDecoder部分是用于处理输入部分的组件层&#xff0c;在这一层中&#xff0c;会对所有输入的input ids进行处理&#xff0c;计算Attention&…

全国大学生数学建模比赛——关联规则

一、问题背景与关联规则适用性 在数学建模比赛中&#xff0c;常常会遇到需要分析大量数据以寻找变量之间潜在关系的问题。关联规则分析作为一种数据挖掘技术&#xff0c;特别适用于这种场景。例如&#xff0c;在一些实际问题中&#xff0c;可能需要从众多的因素中找出哪些因素之…

基于Python的量化交易回测框架Backtrader初识记录(一)

版权声明&#xff1a;本文为博主原创文章&#xff0c;如需转载请贴上原博文链接&#xff1a;基于Python的量化交易回测框架Backtrader初识记录&#xff08;一&#xff09;-CSDN博客 前言&#xff1a;近期以来&#xff0c;对股市数据获取及预处理算是告一段落&#xff0c;下一步…

Java-多线程机制

上篇我提到一些多线程的基本使用方法&#xff0c;但并没有说到底层原理&#xff0c;你或许会有一些疑问&#xff0c;为什么不直接调用Run方法而是要用start方法启动线程&#xff1f;多线程到底是怎样执行的&#xff1f;接下来我会详细带你了解多线程的机制原理。 多线程的底层…

Leetcode114将二叉树转换为链表(java实现)

来看下本题的题目描述&#xff1a; 本题想让我们将二叉树转换为单链表&#xff0c;可以发现&#xff0c;左边的二叉树转换为右边的链表是使用的中序遍历&#xff0c;根 左 右 所以本道题的思路就是可以先用一个集合以中序遍历收集元素&#xff0c;然后再构建单链表。 class S…

[pytorch] --- pytorch基础之tensorboard使用

0 tensorboard介绍 TensorBoard是一个用于可视化机器学习实验结果的工具&#xff0c;可以帮助我们更好地理解和调试训练过程中的模型。 在PyTorch中&#xff0c;我们可以使用TensorBoardX库来与TensorBoard进行交互。TensorBoardX 是一个PyTorch的扩展&#xff0c;它允许我们…

Android Codec2 CCodec (七)IConfigurable

上一篇文章我们了解了接口参数的定义&#xff0c;这一节我们简单梳理一下参数获取、配置以及参数反射过程。 1、IConfigurable 通过之前的介绍我们了解到&#xff0c;Codec2模块的功能实现与配置实现是相互分离的&#xff0c;Codec2框架设计了一组API用于获取与模块关联的配置…

2.4 定时器与TIM中断

文章目录 时钟与时钟树stm32时钟树可以手动把系统时钟72mhz改成其他的吗&#xff1f;ST公司给的外围设备配置文件 的 默认配置说明 定时器什么是定时器定时器的类型 时钟与时钟树 频率&#xff1a;如72Mhz即为每秒72M个脉冲 时钟的源头 晶振 时钟树的分支 分频器&#xff1a;…

超越在线翻译百度!揭秘3款工具,工作学习快人一步

在如今这个信息爆炸的时代&#xff0c;我们经常需要用到不同的语言。无论是看外国文章、写邮件给国外朋友&#xff0c;还是和外国客户聊天&#xff0c;语言不通都是个大问题。不过&#xff0c;科技的进步带来了很多在线翻译工具&#xff0c;百度翻译就是其中很受欢迎的一个。但…

U盘文件目录损坏难读?数据重生方案

在数字化时代的洪流中&#xff0c;U盘作为便携的存储介质&#xff0c;承载着无数人的重要数据与回忆。然而&#xff0c;当U盘不幸遭遇文件或目录损坏&#xff0c;导致数据无法读取时&#xff0c;这份便携与便捷瞬间化为乌有&#xff0c;留下的只有无尽的焦虑与困扰。本文将深入…

爆改YOLOv8|利用可改变核卷积AKConv改进yolov8-轻量涨点

1&#xff0c;本文介绍 AKConv&#xff08;可改变核卷积&#xff09;是一种改进的卷积操作方法&#xff0c;其核心在于动态调整卷积核的形状和大小。与传统卷积层固定核大小不同&#xff0c;AKConv 通过引入可学习的机制&#xff0c;使卷积核在训练过程中能够自适应地调整&…

优雅谈大模型:白话ZeRO 下

机器学习模型的复杂性和规模不断增长&#xff0c;分布式训练变得比以往任何时候都更加重要。训练具有数千亿参数的大型语言模型&#xff08; LLMs &#xff09;将是机器学习基础设施面临的挑战。与传统的分布式计算框架不同的地方在于GPU的分布式训练需要将数据传递给GPU芯片等…

JAVAEE初阶第二节——多线程基础(下)

系列文章目录 JAVAEE初阶第二节——多线程基础(下) 多线程基础(下) 单例模式阻塞式队列定时器线程池 文章目录 系列文章目录JAVAEE初阶第二节——多线程基础(下) 多线程基础(下) 一.多线程案例 1.单例模式1.1 饿汉模式 1.2 懒汉模式 1.2.1 懒汉模式-单线程版1.2.3 懒汉模式…

[Tools: LoRA] Diffusers中Stable Diffusion的实现

实现底层原理 Diffusers中的Attention操作实现在AttnProcessor类&#xff08;diffusers.models.attention_processor.py&#xff09;&#xff0c;里面定义了单次Attention操作。添加LoRA&#xff0c;本质上是用LoRAAttnProcessor类替换AttnProcessor类。LoRAAttnProcessor中新…

github和gitlab的区别是什么

区别&#xff1a;github如果使用私有仓库&#xff0c;是需要付费的&#xff1b;而gitlab可以在上面搭建私人的免费仓库。gitlab让开发团队对他们的代码仓库拥有更多的控制&#xff0c;相对于github&#xff0c;它有不少的特色&#xff1a;允许免费设置仓库权限&#xff1b;可以…

自然语言处理-词向量转换

文章目录 一、简介1.含义2.基本原理3.常见转换方法1&#xff09;. 独热编码&#xff08;One-Hot Encoding&#xff09;2&#xff09;. 词袋模型&#xff08;Bag of Words, BoW&#xff09;3&#xff09;. TF-IDF&#xff08;Term Frequency-Inverse Document Frequency&#xf…