vector模拟实现&&迭代器失效

目录

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进行改写,增加返回值:

  • 17
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值