vector的模拟实现

📌vector的模拟实现

😊本文为万里原创,CSDN首发
📅发布时间:2022/3/20
🙌欢迎大家👍点赞❤收藏✨加关注
✒本文大约2200词左右
🙏笔者水平有限,如有错误,还望告诉笔者,万分感谢!
🚩有什么问题也可在评论区一起交流哦!

📣前言

在熟悉了vector的使用后,让我们来模拟一下vector的实现吧!

(声明:标准库中vector的实现比较复杂,本文旨在加深对vector使用的理解,若按照标准库的标准模拟实现,要牵扯其他的知识,不利于我们学习vector)

🎨模拟实现

一.主干结构

namespace V
{
    //类模板
	template<class T>
	class vector
	{
        //迭代器(指针是一个天然的随机迭代器)
        //关于迭代器,之后我们会详谈
		typedef T* iterator;
	public:
		//...
	private:
        //迭代器可看作一个指针,但也有区别
		iterator _start;//起始位置
		iterator _finish;//最后一个元素下一位置
		iterator _end_of_Storage;//最大容量位置下一位
	};
}

这里是用迭代器去控制vector的,我们知道vector可看作一个可控大小的顺序表,对于顺序表的实现,我们知道有三个参数,一个是指向数组的指针a,一个是数组的大小size,还有一个是数组的容量capacity.所以对于vector来说,我们可以对比着顺序表来看.其实这三个迭代器就类似于顺序表中的三个参数.start指向内存空间起始位置,finish指向最后一个元素的下一位,end_of_storage指向最大容量位置下一位.

在这里插入图片描述

二.成员函数

🚀取容量 capacity()
size_t capacity() const 
		{
			return _end_of_Storage - _start;
		}
🚀取大小 size()
size_t size() const
		{
			return _finish - _start;
		}
🚀swap
void swap(vector<T>& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_Storage, v._end_of_Storage);
		}
🚀构造函数(1)
vector()
	:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
	{}
🚀析构函数
~vector()
	{
		//_start != nullptr
		if (_start)
		{
			delete[] _start;
			_start = _finish = _end_of_Storage = nullptr;
		}
	}
🚀拷贝构造函数

Traditional

vector(const vector<T>& v)
		{
			_start = new T[v.capacity()];
			_finish = _start + v.size();
			_end_of_Storage = _start + v.capacity();
			memcpy(_start, v._start, v.size()*sizeof(T));
		}

Modern

vector(const vector<T>& v)
		:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
		{
         //调用构造函数
			vector<T> tmp(v.begin(), v.end());
         //自己实现的swap效率高,若用库里的swap(v1,v2),会有3次深拷贝
			swap(v);
		}

这里需要说明的是,对于vector拷贝构造函数的现代写法的思想与之前string的拷贝构造函数的现代写法思想一致,就是希望定义一个临时对象,其调用构造函数来开辟一块空间,但是对于vector来说,它在这里就不能用一开始写的构造函数了.

对于该使用需求,vector还有一种构造函数,它是通过一个迭代器区间初始化.

🚀构造函数(2)
//对于InputIterator的取名不能随意改,这是规范
//一个类模板的成员函数,也可以是一个函数模板
template<class InputIterator>
vector(InputIterator first, InputIterator last)
			:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
		{
			while (first != last)
			{
            //尾插,下文给出
				push_back(*first);
				first++;
			}
		}
🚀尾插 push_back
void push_back(const T& x)
		{
			if (_finish == _end_of_Storage)
			{
            //增容
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			_finish++;
		}
🚀增容 reserve

对于增容reserve函数,这里有一个易错点,以下是错误代码,大家能看出哪儿错了吗?

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*size());
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + size();
				_end_of_Storage = _start + n;
			}
		}

让我们揭晓答案~~

void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*size());
					delete[] _start;
				}
				_start = tmp;
            //size()函数内部的实现是_finish-_start
            //原空间被释放后,_start指向新开辟的空间的起始位置,而_finish还是指向原来的空间位置
            //故在求size()时答案错误
				_finish = _start + size();
				_end_of_Storage = _start + n;
			}
		}

不难发现,问题就出在求size()的时候start的位置更新了,要解决这个问题就是要保持start和finish的相对位置,或是保存一份size(),所以这里就有两种解决方法:

//写法一:
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)
				{
                    //内存拷贝(字节序拷贝)
					memcpy(tmp, _start, sizeof(T)*size());
					delete[] _start;
				}
				//求得size()后再更新_start
				_finish = tmp + size();
				_start = tmp;
				_end_of_Storage = _start + n;
			}
		}
//写法二:
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				//保存一份size
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*sz);
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_Storage = _start + n;
			}
		}
🚀重置大小 resize()
 //缺省值是一个匿名对象,调用了它自己的构造函数
void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);//增容
				}
				while (_finish != _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}

对于resize()的实现,我相信大家都看得懂,但是大家可能会有一个疑惑:

🔎这里使用了匿名对象传缺省值,匿名对象的生命周期不是只在当前行吗?

🔎val是T()的引用,那该行结束,T()生命周期结束,val不也应该失效吗?

🔎那为什么程序没有出错?

🔎如果T是像int,double这样的内置类型,它们也有自己的构造函数?

为了不影响本文的篇幅和结构,关于匿名对象生命周期的问题,我在下面链接所指的文章中有详细的解释,感兴趣的老铁可以点击链接,前去查看~~

关于匿名对象生命周期的讨论

🚀赋值运算符重载 operator=
vector<T>& operator=(const vector<T> v)//v是实参的拷贝构造出来的临时对象
		{                                //函数调用完自动调用析构函数销毁
			swap(v);
			return *this;
		}
🚀插入 insert
iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start&&pos <= _finish);
			if (_finish == _end_of_Storage)
			{
				//保存pos位置,防止增容后pos位置失效
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;//更新pos位置
			}
			iterator end = _finish;
			while (end != pos)
			{
				*end = *(end - 1);
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}

对于插入操作,要注意的就是增容带来的迭代器失效问题!

不仅如此,我们发现insert还返回了插入的位置,原因是insert的pos实参到形参是传值,如果增容了,函数里的pos更新了,但是外面的pos没有更新,为了不让外面的pos迭代器失效,insert支持了返回更新后的pos.

🚀尾删 pop_back
void pop_back()
		{
			assert(_finish > _start);
			_finish--;
		}
🚀删除 erase
iterator erase(iterator pos)
		{
			assert(pos >= _start&&pos < _finish);
			iterator cur = pos + 1;
			while (cur < _finish)
			{
				*(cur - 1) = *cur;
				cur++;
			}
			_finish--;
			return pos;//更新pos位置
		}

三.完整代码

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

namespace V
{
	template<class T>
	class vector
	{
		typedef T* iterator;
	public:
		size_t capacity() const 
		{
			return _end_of_Storage - _start;
		}

		size_t size() const
		{
			return _finish - _start;
		}

		vector()
			:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
		{}
		~vector()
		{
			//_start != nullptr
			if (_start)
			{
				delete[] _start;
				_start = _finish = _end_of_Storage = nullptr;
			}
		}
      
		//Modern写法
		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(const vector<T>& v)
		:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
		{
			vector<T> tmp(v.begin(), v.end());
			swap(v);
		}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
			:_start(nullptr), _finish(nullptr), _end_of_Storage(nullptr)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}

		void push_back(const T& x)
		{
			if (_finish == _end_of_Storage)
			{
				reserve(capacity() == 0 ? 4 : capacity() * 2);
			}
			*_finish = x;
			_finish++;
		}

		//写法一:
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*size());
					delete[] _start;
				}
				//求得size()后再更新_start
				_finish = tmp + size();
				_start = tmp;
				_end_of_Storage = _start + n;
			}
		}
		//写法二:
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				//保存一份size
				size_t sz = size();
				T* tmp = new T[n];
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T)*sz);
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_Storage = _start + n;
			}
		}
      
        //缺省值是一个匿名对象,调用了它自己的构造函数
		void resize(size_t n, const T& val = T())
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);//增容
				}
				while (_finish != _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}
		}

		vector<T>& operator=(const vector<T> v)
		{
			swap(v);
			return *this;
		}

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

		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start&&pos <= _finish);
			if (_finish == _end_of_Storage)
			{
				//保存pos位置,防止增容后pos位置失效
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : capacity() * 2);
				pos = _start + len;//更新pos位置
			}
			iterator end = _finish;
			while (end != pos)
			{
				*end = *(end - 1);
				end--;
			}
			*pos = x;
			_finish++;
			return pos;
		}

		iterator erase(iterator pos)
		{
			assert(pos >= _start&&pos < _finish);
			iterator cur = pos + 1;
			while (cur < _finish)
			{
				*(cur - 1) = *cur;
				cur++;
			}
			_finish--;
			return pos;//更新pos位置
		}

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_Storage;
	};
}

🎶代码练习

大家可以将下面代码拷贝到编译器上,自己写一写,练习一下!

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

namespace V
{
	template<class T>
	class vector
	{
		typedef T* iterator;
	public:
      //取容量
		size_t capacity() const 
		
		//取大小
		size_t size() const
		
		//构造函数
		vector()
			
      //析构函数
		~vector()

      //拷贝构造函数
		vector(const vector<T>& v)

      //迭代器实现构造函数
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)

      //尾插
		void push_back(const T& x)

		//增容(两种写法)
		void reserve(size_t n)

      //重置大小
		void resize(size_t n, const T& val = T())

      //赋值运算符重载
		vector<T>& operator=(const vector<T> v)

      //pos位置插入数据
		iterator insert(iterator pos, const T& x)

      //删除pos位置元素
		iterator erase(iterator pos)

	private:
		iterator _start;
		iterator _finish;
		iterator _end_of_Storage;
	};
}

文章到这就结束了,大家有什么问题欢迎在评论区讨论,博主一定及时回复!觉得文章写的不错的老铁也可以点赞收藏加关注,后续还会有更多的好文分享哦!

  • 20
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 34
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱敲代码的小邢~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值