c++:vectro模拟实现(详细)

我们模拟实现vector不是为了造更好的轮子,而是通过模拟更加理解vector这个容器

我们模拟实现vector不是为了造更好的轮子,而是通过模拟更加理解vector这个容器

我们模拟实现vector不是为了造更好的轮子,而是通过模拟更加理解vector这个容器

目录

 第一步:创建两个头文件

第二步:设定各种初始函数

 第三步:实现尾插(push_back)

第四步:打印数据

 第五步:尾删(pop_back)

第六部:resize的实现

第七步:insert的模拟实现(迭代器失效) 

第八步:erase的模拟实现

第九步:swap函数与重载

第十步:析构函数

总代码 


-------------------------------------------------------

 第一步:创建两个头文件

对于模拟实现vector,首先我们先建立两个文件,一个头文件vector.h用来装载需要的函数,另一个头文件 test.cpp用来测试 

第二步:设定各种初始函数

其实对于vector 我们目前大体上是知道模板这个东西的。

备注:不知道模板的小伙伴可以看我的这篇文章c++模板简介_幻荼的博客-CSDN博客

同时我们一般使用三个指针来对模板数组进行构造

他们分别是start,finish,endofsoage(我这里是按照官方的命名方式)

简单来说就是这个(我也不知道为什么要这样命名,大佬这样做一定有他的道理)

namespace bit//我这里自己命名了一个空间bit,你按照自己的喜欢命名就好
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;//const和非const两个迭代器
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)//初始化
			,_finish(nullptr)
			,_endofstoage(nullptr)
		{}
	private:
		iterator _start;//起始位置
		iterator _finish;//size
		iterator _endofstoage;//capacity
	};
}

 第三步:实现尾插(push_back)

其实尾插很简单,我们只需要考虑一个问题,就是容量的问题。

对于任何数据的改动,我们都需要知道究竟size和capacity的关系改动后是如何。

如果size>=capacity那么我们就需要进行扩容

void push_back(const T& x)
		{
			if (_finish == _endofstoage)//判断size!=capacity
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;//扩容
				reserve(newCapacity);//通过reseve将旧空间的数据拷到新空间
			}
			*_finish = x;//尾插
			++_finish;//size++
		}

 因为是自己模拟实现vector,所以vector中的reserve也需要我们自己实现

备注:对于不了解原版的reseve以及size和capacity的小伙伴可以看我这篇文章

c++:reserve和resize简介和区别_幻荼的博客-CSDN博客

	void reserve(size_t n)
		{
			size_t sz = size();
			if (n > capacity())
			{
				T* tmp = new T[n];//开辟新空间
				if (_start)//如果是空数据就不用拷贝和删除了
				{
					memcpy(tmp, _start, sizeof(T) * size());//拷贝数据
					delete[]_start;//删除旧空间
				}
				_start = tmp;//指向新空间
			}
			_finish = _start + sz;
			_endofstoage = _start + n;
		}

备注:不了解memcpy的小伙伴可以看我这篇文章了解与模拟实现memmove和memcpy_幻荼的博客-CSDN博客

不了解new的小伙伴可以看我这篇文章

c++:new的简析_幻荼的博客-CSDN博客

第四步:打印数据

我们先在头文件,vector.h里面push_back一些数据进入

void test_vector1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);//我这里插入5个是因为我扩容是从4开始的,我要测试一下扩容是否成功
	}

对于输出数据我这里用两种方式,一种范围for,一种迭代器,至于下标+[]的方式就不做演示了。

对于迭代器的方式,我们弄个begin和end然后while循环一下就好

iterator begin()
		{
			return _start;
		}
iterator end()
		{
			return _finish;
		}
auto it = v.begin();//vector<int>::iterator it = v.begin()简化一下,这是原版
while (it != v.end())//it指向最开始,当it!=最后一个,那么while就一直下去
		{
			cout << *it << " ";
			it++;
		}

然后范围for

for (auto e : v)
		{
			cout << e << " ";
		}

最后再test.cpp中对该函数进行测试

int main()
{
	bit::test_vector1();
	return 0;
}

结果如下:

 第五步:尾删(pop_back)

.....实在没什么需要讲的

void pop_back()
		{
			if (_finish > _start)
			{
				--_finish;
			}
		}

第六部:resize的实现

因为resize有两种情况

第一种:n>capacity,需要我们扩容(同时n>size还需要赋初始值);

第二种:n<size,需要我们缩容;

void resize(size_t n,const T& val=T())
		{
			if (n > capacity())//扩容
			{
				reserve(n);
			}
			if (n > size())//赋值
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
			else//缩容
			{
				_finish = _start + n;
			}
		}

备注:不了解resize和reserve的小伙伴看我的这篇文章c++:reserve和resize简介和区别_幻荼的博客-CSDN博客

第七步:insert的模拟实现(迭代器失效) 

实际上前面的六步都是没有什么难度的,大家也都能很容易的写出来,但我本人学习的时候再insert和erase这里跌了一个坑,弄了很久才想明白。

这也就牵扯到一个叫做”迭代器失效的问题”。

首先先来看一个我最开始的错误代码 

void insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);//检查
			if (_finish == _endofstoage)//扩容
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}
			iterator end = _finish-1;//挪动数据
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = x;
			_finish++;
		}

实际上是这么个情况:我让end=末尾数据的-1的位置,让后依次挪动数据,当最后一次end=pos的位置数据被挪动,就是v[1]=v[0],然后end--=-1,最后将数据插入进pos的位置。

然后接下来就有一个很神奇的事情

当我调试插入4个数据(就是我扩容的临界值) ,编译器,崩了

        v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.insert(v.begin(), 0);
		for (auto e : v)
		{
			cout << e << " ";
		}

 然后我插入5个值,他又好了

v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		v.insert(v.begin(), 0);
		for (auto e : v)
		{
			cout << e << " ";
		}

 然后我就以为是我扩容那一段写错了,对照着各路扩容代码改了半天,结果是迭代器失效

最根本的原因是在扩容reserve的这里

void reserve(size_t n)
		{
			size_t sz = size();
			if (n > capacity())
			{
				T* tmp = new T[n];//罪魁祸首
.........

我们在使用reserve扩容的时候,更新了_start,_finish,_endofstorage到新空间,但是唯独没有更新pos,pos还是指向了原先被释放的旧空间

这是开始没扩容的时候

 这是扩容后

 大家可以看到前三个已经到了新空间,而pos还是在原先的旧空间

这就是非常经典的迭代器失效问题

解决也很简单,只需要更新一下pos就好

void insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _endofstoage)
			{
				size_t n = pos - _start;//新加入,算相对路径
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
				pos = _start + n;//新加入,把相对路径给新空间的pos
			}
			iterator end = _finish-1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = x;
			_finish++;
		}

结果如下,就没问题了

既然讲都讲到了迭代器失效,那我们再看一个我模拟实现的时候遇到的迭代器失效问题。

在偶数前面都插入一些数字,假如20.

void test_vector3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)//判断偶数
			{
				v.insert(it, 20);
			}
			it++;
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
	}

 然后啪的一下,很快啊,一个错误给我报了出来

 经过调试我发现,20插入了v[1]之后,it++这个地方出现了问题,

 经过了insert扩容之后,it已经不在_start和_finish的区间了

看到这个朋友们就会有疑问

我不是已经在insert里面更新了pos吗,为什么it把值传给pos之后,it还会指向旧空间呢?

答案也很简单

 pos这里是值传递,形参不改变实参

那我加个&是否可以呢?

如果加了&有些地方就会报错,而且我们是模拟实现vector,官方里面都没加&,我们最好也不要加

 那有些小伙伴可能又要说了

那么我们开一个足够大的空间,让他不扩容不就好了吗?

让我们试试

	void test_vector3()
	{
		vector<int> v;
		v.reserve(10);//新加入
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				v.insert(it, 20);
			}
			it++;
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
	}

啪的一下很快啊,编译器又把报错甩给我了

 这里就是另外一个迭代器失效的问题了

我们在每次it插入之后,_start,_finish,_endofstorage都更新了,这就导致我们it每次++,都是从

v[1]->v[2],然后一直在v[2]处插入数据

 所以:

insert以后虽然没有扩容,it也没有成为野指针,但是it指向的意义已经发生改变,所以这也是迭代器失效

这两个问题,实际上也很好解决,将insert的值给it,然后it+2就行了

void test_vector3()
	{
		vector<int> v;
		v.reserve(10);
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		auto it = v.begin();
		while (it != v.end())
		{
			if (*it % 2 == 0)
			{
				it=v.insert(it, 20);//将insert的值给it,然后it+2
				++it;
			}
				it++;
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
	}
}

 总结:vector迭代器失效有两种

1.扩容,缩容,导致野指针

2.迭代器指向的位置意义变了

系统越界机制检查——不一定检查得到

编译实现机制检查——相对严格

第八步:erase的模拟实现

对于已经实现的insert,erase只需要在insert的基础上更改一下就可以了

iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				it++;
			}
			_finish--;
			return pos;
		}

实际上就是这样:

第九步:swap函数与重载

经过以上几步,我们已经吧vector常用的函数功能实现的差不多了,下面就是对运算符等一系列资源进行重载

对于重载,我们这里直接介绍一种资本家写法

vector<T>& operator=(vector<T> v)
		{
			this->swap(v);
			return *this;
		}
void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finfish);
			std::swap(_endofstoage, v._endofstorage);
		}
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			this->swap(tmp);
		}

 简单来说,就是我先创建一个和你一样大的vector

然后先给这个vector赋一个空值,然后将数据拷贝进入新建的vector。

将新建的vector数据和老vector数据交换

最后释放新建的vector,老的vector也完成了数据交换

就好比辛亥革命,袁世凯窃取革命的劳动成果基本上没花费什么力气。



第十步:析构函数

没什么需要解释的

~vector()
		{
			if (_start)
			{
				delete[]_start;
				_start = _finish = _endofstoage = nullptr;
			}
		}

最后附上总代码

总代码 

vector.h:

#pragma once
#include<vector>
#include<string>
#include<iostream>
#include<algorithm>
#include<functional>
#include<assert.h>
using namespace std;
namespace bit
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
		vector()
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{}
		~vector()
		{
			if (_start)
			{
				delete[]_start;
				_start = _finish = _endofstoage = nullptr;
			}
		}
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}
		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finfish);
			std::swap(_endofstoage, v._endofstorage);
		}
		vector(const vector<T>& v)
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstoage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			this->swap(tmp);
		}
		vector<T>& operator=(vector<T> v)
		{
			this->swap(v);
			return *this;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		size_t capacity()const
		{
			return _endofstoage - _start;
		}
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin()const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}
		void reserve(size_t n)
		{
			size_t sz = size();
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T) * size());
					delete[]_start;
				}
				_start = tmp;
			}
			_finish = _start + sz;
			_endofstoage = _start + n;
		}
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}
		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}
		void clear()
		{
			_finish = _start;
		}
		void pop_back()
		{
			/*if (_finish > _start)
			{
				--_finish;
			}*/
			erase(end()-1);
		}
		void resize(size_t n,const T& val=T())
		{
			if (n > capacity())
			{
				reserve(n);
			}
			if (n > size())
			{
				while (_finish < _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
			else
			{
				_finish = _start + n;
			}
		}
		void push_back(const T& x)
		{
			/*if (_finish == _endofstoage)
			{
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
			}
			*_finish = x;
			++_finish;*/
			insert(end(), x);
		}
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start && pos <= _finish);
			if (_finish == _endofstoage)
			{
				size_t n = pos - _start;
				size_t newCapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newCapacity);
				pos = _start + n;
			}
			iterator end = _finish-1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}
		iterator erase(iterator pos)
		{
			assert(pos >= _start && pos < _finish);
			iterator it = pos + 1;
			while (it != _finish)
			{
				*(it - 1) = *it;
				it++;
			}
			_finish--;
			return pos;
		}
	private:
		iterator _start;
		iterator _finish;
		iterator _endofstoage;
	};
	void test_vector1()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.push_back(5);
		auto it = v.begin();
		while (it != v.end())
		{
			cout << *it << " ";
			it++;
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
		cout << endl;
	}
	void test_vector2()
	{
		vector<int> v;
		/*v.resize(10, -1);
		for (auto e : v)
		{
			cout << e << " ";
		}*/
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		v.insert(v.begin(), 0);
		for (auto e : v)
		{
			cout << e << " ";
		}
	}
	void test_vector3()
	{
		vector<int> v;
		v.push_back(1);
		v.push_back(2);
		v.push_back(3);
		v.push_back(4);
		vector<int>::iterator it = v.begin();
		auto pos = find(v.begin(), v.end(), 2);
		if (pos != v.end())
		{
			v.erase(pos);
		}
		for (auto e : v)
		{
			cout << e << " ";
		}
	}
}

test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include"vector.h"
using namespace std;
int main()
{
	bit::test_vector1();
	return 0;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值