C++ | vector的模拟实现

目录

前言

一、大体框架

二、构造函数与析构函数

三、迭代器

 四、容量相关接口 

五、访问相关接口

六、修改对象相关接口

七、源码 


前言

        前面我们学习了如何使用vector的各种接口,为了深层次的理解掌握vector,本文模拟实现一个vector,实现vector主要的核心接口,并可以用我们实现的vector来做题(我们的标准);

一、大体框架

        我们的实现模仿SGI版本的vector,因此,命名上与实现方式也类似;一下为vector的大体框架;

namespace MySpace
{
	template<class T>
	class vector
	{
		// 定义vector迭代器
		typedef T* iterator;
		typedef const T* const_iterator;
	public:

	private:
		// 指向第一个元素位置的指针
		iterator _start = nullptr;
		// 指向最后一个元素位置的指针
		iterator _finish = nullptr;
		// 指向最大容量的下一个位置的指针
		iterator _end_of_storage = nullptr;
	};

        我们通过三个指针来管理这段连续的内存空间,完成这个容器模拟增删查改的过程;

二、构造函数与析构函数

构造函数实现了有如下几个;

		// 构造函数
		vector()
			:_start(nullptr)
			,_finish(nullptr)
			,_end_of_storage(nullptr)
		{}
		vector(size_t n, const T& val = T())
		{
			reserve(n);
			for(size_t i = 0; i < n; i++)
			{
				_start[i] = val;
			}
			_finish = _start + n;
		}
		// 提供重载版本防止调用到下面迭代器构造
		vector(int n, const T& val = T())
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				_start[i] = val;
			}
			_finish = _start + n;
		}

		template<class InputIterator>
		vector(InputIterator first, InputIterator last)
		{
			while (first != last)
			{
				push_back(*first);
				first++;
			}
		}
		// 拷贝构造
		vector(const vector& v)
		{
			T* tmp = new T[v.capacity()];
			// 不能使用memcpy,因为这里需要深拷贝
			for (size_t i = 0; i < v.size(); i++)
			{
				// 这里使用了[]重载(后面会实现)
				// 如果vector中存的是内置类型,则=是赋值,如果vector中存的是自定义类型,则会调用自定义类型的赋值重载
				tmp[i] = v[i];
			}
			_start = tmp;
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}

		 赋值重载(传统写法)
		//vector& operator=(const vector& v)
		//{
		//	T* tmp = new T[v.capacity()];

		//	// 注意深拷贝
		//	for (size_t i = 0; i < v.size(); i++)
		//	{
		//		tmp[i] = v._start[i];
		//	}

		//	delete[] _start;
		//	_start = tmp;
		//	_finish = _start + v.size();
		//	_end_of_storage = _start + v.capacity();
		//	return *this;
		//}
		
		// 赋值重载(现代写法)
		vector& operator=(vector v)
		{
            // 这里的swap也是后面实现的swap,不用算法库中的swap是为了提高效率
			swap(v);
			return *this;
		}
		~vector()
		{
			delete[] _start;
			_start = _finish = _end_of_storage = nullptr;
		}

        其中第三个构造函数重载的意义是重载参数为int类型的n,因为当构造函数参数都为两个int时,它会匹配到迭代器那个构造函数中去;

注意:所有需要拷贝的地方都需要想一想是否需要用深拷贝,如果用深拷贝,则必须要考虑怎么深拷贝,不能用mencpy进行拷贝,memcpy拷贝是浅拷贝;

三、迭代器

迭代器有以下接口,本文暂时实现正向迭代器与const修饰的迭代器;

		// 迭代器
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}
		// 返回const迭代器
		const_iterator begin() const
		{
			return _start;
		}
		const_iterator end() const
		{
			return _finish;
		}

 四、容量相关接口 

有如下几个容量相关接口,这里也挑一些常用的实现;

		// capacity
		// 指针相减,得到就是中间元素个数
		size_t size() const
		{
			return _finish - _start;
		}
		size_t capacity() const
		{
			return _end_of_storage - _start;
		}
		bool empty()
		{
			return _start == _finish;
		}
		void reserve(size_t n)
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				// 注意:这里不能用memcpy!!因为memcpy是浅拷贝!
				// 如果vector中存的不是内置类型,可能会出现不可预知的错误
				for (size_t i = 0; i < size(); i++)
				{
					// 如果是内置类型则赋值,如果是自定义类型,则调用其赋值重载
					tmp[i] = _start[i];
				}
				size_t len = size();
				// 释放原来就空间
				delete[] _start;
				_start = tmp;
				// _finish = _start + size(); err
				// 上式等同与 _finish = _start + _finish - _start;  
				// 而且由于此时start已经发生改变,计算的结果未知
				_finish = _start + len;
				_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);
				}
				for (size_t i = size(); i < n; i++)
				{
					// 对于自定义类型,调用赋值重载进行深拷贝
					_start[i] = val;
				}
				_finish = _start + n;
			}
		}

        resize与reserve这两个函数都是需要考虑在不同的参数下,做出的反应是什么,要分别考虑参数n小于size,大于size而小于capacity,大于capacity这三种情况;我们的原则与库里的原则一致,能不缩容,坚决不所容,因为缩容所消耗的代价太大了;

五、访问相关接口

        在访问vector时,我们常常会提供 [ ] 重载,使其访问行为与数组一致,同时我们还会提供一个at接口,at与[]的功能一致,当提供参数非法时,at通常是抛异常,而 [ ] 则是粗暴的断言方式;

		// access
		T& operator[](size_t i)
		{
			assert(i >= 0);
			assert(i < size());
			return _start[i];
		}
		const T& operator[](size_t i) const
		{
			assert(i >= 0);
			assert(i < size());
			return _start[i];
		}
		T& front()
		{
			return _start[0];
		}
		const T& front() const
		{
			return _start[0];
		}
		T& back()
		{
			return _start[size() - 1];
		}
		const T& back() const
		{
			return _start[size() - 1];
		}

        同时,每个访问接口我们都提供了两个版本,一个版本是用于const对象,一个则是普通对象调用的函数;

六、修改对象相关接口

        最后一批接口为修改对象的一批接口,通常是对对象进行增删的接口,查找接口通常用算法库中的find函数即可;

		// modify
		void push_back(const T& val)
		{
			// 查看是否需要扩容
			if (_finish == _end_of_storage)
			{
				reserve(capacity() + 1);
			}
			*_finish = val;
			++_finish;
		}
		void pop_back()
		{
			assert(size() > 0);
			--_finish;
		}
		void insert(iterator pos, const T& val)
		{
			// 检查是否需要扩容
			if (size() + 1 == capacity())
			{
				// 如果扩容了要更新pos位置(迭代器失效问题)
				size_t len = pos - _start;
				reserve(capacity() * 2);
				pos = _start + len;
			}
			iterator end = _finish;
			while (end > pos)
			{
				*end = *(end - 1);
				end--;
			}
			*pos = val;
			_finish++;
		}
		void insert(iterator pos, size_t n, const T& val)
		{
			// 检查是否需要扩容
			if (size() + n > capacity())
			{
				// 扩容后要更新pos位置(迭代器失效问题)
				size_t len = pos - _start;
				reserve(size() + n);
				pos = _start + len;
			}
			iterator end = _finish + n - 1;
			while (end >= pos + n)
			{
				*end = *(end - n);
				end--;
			}
			end = pos;
			for (size_t i = 0; i < n; i++)
			{
				*end = val;
				end++;
			}
			_finish += n;
		}

		void erase(iterator pos)
		{
			assert(pos >= _start);
			assert(pos < _finish);
			iterator end = pos;
			while (end < _finish - 1)
			{
				*end = *(end + 1);
				end++;
			}
			--_finish;
		}
		void swap(vector& v)
		{
			std::swap(_start, v._start);
			std::swap(_finish, v._finish);
			std::swap(_end_of_storage, v._end_of_storage);
		}
		void clear()
		{
			_finish = _start;
		}

        其中,尤其需要注意的是在insert中出现的迭代器失效的问题;如果我们扩容了,扩容前,我们要保存其相对位置,因为扩容后,pos指向的是之前需要扩容的位置;

七、源码 

 由于代码庞大,本文所有源码都保存在了gitee上,可通过以下链接来访问;

vector模拟实现源码

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值