【STL】:vector的模拟实现

朋友们、伙计们,我们又见面了,本期来给大家解读一下有关vector的模拟实现,如果看完之后对你有一定的启发,那么请留下你的三连,祝大家心想事成!

C 语 言 专 栏:************C语言:从入门到精通****************

数据结构专栏:************数据结构****************

个 人 主 页 :************stackY、****************

C + + 专 栏 :C++****

Linux 专 栏 :Linux****

目录

[1. 基本构造](#1. 基本构造)

[2. 容量相关的接口](#2. 容量相关的接口)

[2.1 operator[ ]](#2.1 operator[ ])

[2.2 reserve](#2.2 reserve)

[2.3 resize](#2.3 resize)

[2.4 size、capacity](#2.4 size、capacity)

[3. 迭代器](#3. 迭代器)

[4. 修改相关接口](#4. 修改相关接口)

[4.1 insert、push_back](#4.1 insert、push_back)

[4.2 erase](#4.2 erase)

[5. 拷贝构造和赋值重载和其他构造](#5. 拷贝构造和赋值重载和其他构造)

[5.1 拷贝构造](#5.1 拷贝构造)

[5.2 赋值重载](#5.2 赋值重载)

[5.3 其他构造](#5.3 其他构造)

[6. 完整代码](#6. 完整代码)


1. 基本构造

在实现vector之前可以先看一下库里面的vector是如何实现的:

cpp 复制代码
#pragma once
#include <assert.h>

namespace ywh
{
	template<class T>
	class vector
	{
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		/基本构造///
		//构造
		vector()
		{}
		//析构
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
	private:
		iterator _start = nullptr;   //起始
		iterator _finish = nullptr;  //有效数据个数
		iterator _end_of_storage = nullptr; //有效空间
	};
}

2. 容量相关的接口

2.1 operator[ ]

cpp 复制代码
//operator[]
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

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

2.2 reserve

关于reserve的实现最主要的就是深拷贝与浅拷贝的问题,使用浅拷贝对于内置类型很容易处理,但是对于自定义类型就会出现麻烦:

需要进行深拷贝,关于自定义类型的赋值重载就是一个深拷贝,所以我们需要使用深拷贝来说实现reserve:

cpp 复制代码
//reserve
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//浅拷贝
					//对于自定义类型不能处理
					//memcpy(tmp,_start,sizeof(T)*sz);
					//采用深拷贝
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}	
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}

2.3 resize

cpp 复制代码
//resize
		//这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

2.4 size、capacity

有效元素的个数从开始位置到数据截止位置

容量是开始位置到空间终止位置

cpp 复制代码
//容量
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		//有效个数
		size_t size() const
		{
			return _finish - _start;
		}

3. 迭代器

关于模拟实现的vector只实现正向迭代器,反向迭代器的实现会在stack和queue的适配器模式中详解

cpp 复制代码
/迭代器
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

4. 修改相关接口

4.1 insert、push_back

在insert的时候需要注意的是:假如需要扩容,那么pos还是在旧空间,所以在扩容之前需要将旧空间pos的偏移量记录出来,然后在扩容之后将新空间的pos记录出来。

cpp 复制代码
//在pos位置插入
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				//记录旧空间中pos的偏移量
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				//找出新空间的pos
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//尾插
		void push_back(const T& x)
		{
			//复用插入
			insert(end(), x);
		}

insert之后的迭代器会失效,上述中的pos就是迭代器失效的一种案例,在不同的平台下对于迭代器的失效都有不同的检查方法,VS2019上如果使用insert之后的迭代器是不允许进行使用的。

4.2 erase

cpp 复制代码
	//删除pos位置
		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
		}

如果我们单纯的看这个代码会感觉erase之后迭代器是不会失效的,但是erase之后迭代器失效会在特殊情况下展示出来,比如要删除偶数:

cpp 复制代码
void Test()
{
	ywh::vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);

	ywh::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
        //删除偶数
		if (*it % 2 == 0)
		{
			v.erase(it);
		}
		it++;
	}

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

在这里有三种情况:

我们可以看看库里面是如何设计的:

对于返回值的介绍:

返回被删除数据的下一个位置

所以我们需要对我们实现的erase进行调整:

cpp 复制代码
//删除pos位置
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			//返回删除位置的后一个位置
			return pos;
		}
cpp 复制代码
void Test()
{
	ywh::vector<int> v;
	v.push_back(2);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	v.push_back(5);
	v.push_back(6);

	ywh::vector<int>::iterator it = v.begin();
	while (it != v.end())
	{
		if (*it % 2 == 0)
		{
			//删除后自动找到删除位置的下一个位置
			v.erase(it);
		}
		else
		{
			//不符合情况再往后面走
			it++;
		}
	}

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

5. 拷贝构造和赋值重载和其他构造

5.1 拷贝构造

cpp 复制代码
//拷贝构造
		vector(const vector<T>& v)
		{
            //开辟空间
			reserve(v.capacity());
            //依次尾插
			for (auto e : v)
			{
				push_back(e);
			}
		}

5.2 赋值重载

cpp 复制代码
void swap(vector<T>& v)
{
	std::swap(_start, v._start);
	std::swap(_finish, v._finish);
	std::swap(_end_of_storage, v._end_of_storage);
}
vector<T>& operator=(vector<T> tmp) 
{
	//直接交换
	swap(tmp);
	return *this;
}

5.3 其他构造

cpp 复制代码
//迭代器区间初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//n个val构造
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}

6. 完整代码

头文件:Vector.h

cpp 复制代码
#pragma once
#include <assert.h>

namespace ywh
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;
	public:
		/迭代器
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

		/基本构造///
		//构造
		vector()
		{}

		//迭代器区间初始化
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}

		//n个val构造
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				//n个val进行尾插
				push_back(val);
			}
		}

		//拷贝构造
		vector(const vector<T>& v)
		{
			//开辟空间
			reserve(v.capacity());
			//依次尾插
			for (auto e : v)
			{
				push_back(e);
			}
		}


		void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
        //operator=
		vector<T>& operator=(vector<T> tmp) 
		{
			//直接交换
			swap(tmp);
			return *this;
		}

		//析构
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}
		///容量
		//operator[]
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}

		const T& operator[](size_t pos) const
		{
			assert(pos < size());
			return _start[pos];
		}
		//容量
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		//有效个数
		size_t size() const
		{
			return _finish - _start;
		}
		//reserve
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					//浅拷贝
					//对于自定义类型不能处理
					//memcpy(tmp,_start,sizeof(T)*sz);
					//采用深拷贝
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}	
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}
		//resize
		//这里使用匿名对象进行缺省值的传参,由于匿名函数具有常性所以要使用const
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				reserve(n);
				while (_finish < _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}

		///修改
		//在pos位置插入
		void insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			if (_finish == _end_of_storage)
			{
				//记录旧空间中pos的偏移量
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				//找出新空间的pos
				pos = _start + len;
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;
		}
		//尾插
		void push_back(const T& x)
		{
			//复用插入
			insert(end(), x);
		}
		//删除pos位置
		iterator erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			
			iterator it = pos + 1;
			while (it < _finish)
			{
				*(it - 1) = *it;
				++it;
			}
			--_finish;
			//返回删除位置的后一个位置
			return pos;
		}

	private:
		iterator _start = nullptr;   //起始位置
		iterator _finish = nullptr;  //有效数据位置
		iterator _end_of_storage = nullptr; //结束位置
	};
}

朋友们、伙计们,美好的时光总是短暂的,我们本期的的分享就到此结束,欲知后事如何,请听下回分解~,最后看完别忘了留下你们弥足珍贵的三连喔,感谢大家的支持!

相关推荐
Kisorge10 分钟前
【C语言】指针数组、数组指针、函数指针、指针函数、函数指针数组、回调函数
c语言·开发语言
轻口味1 小时前
命名空间与模块化概述
开发语言·前端·javascript
晓纪同学2 小时前
QT-简单视觉框架代码
开发语言·qt
威桑2 小时前
Qt SizePolicy详解:minimum 与 minimumExpanding 的区别
开发语言·qt·扩张策略
飞飞-躺着更舒服2 小时前
【QT】实现电子飞行显示器(简易版)
开发语言·qt
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 16课题、并发编程
开发语言·青少年编程·并发编程·编程与数学·goweb
明月看潮生2 小时前
青少年编程与数学 02-004 Go语言Web编程 17课题、静态文件
开发语言·青少年编程·编程与数学·goweb
Java Fans2 小时前
C# 中串口读取问题及解决方案
开发语言·c#
盛派网络小助手3 小时前
微信 SDK 更新 Sample,NCF 文档和模板更新,更多更新日志,欢迎解锁
开发语言·人工智能·后端·架构·c#
Chinese Red Guest3 小时前
python
开发语言·python·pygame