Vector的实现

从本质上看Vector就是数组,一种顺序存储的容器,c++将它作为一种类封装更便于使用

在c++标准库里面可以看到它的实现,和我们经常用的数组不同,库里面并不是使用我们熟知的指向首元素的指针+元素个数+容量大小,而是使用了三个指针,分别指向的是,首元素,最后一个元素末尾,容量结束末尾。

为了能够使用更多种类型,可以写一个模板,而这三个指针也应当用传统的迭代器,迭代器的类型则是模板实例化后的指针类型。

在模拟实现时为避免和库里的Vector混淆,可以在外面加一层命名空间。

​
namespace zz
{
	template<class type>
	class Vector
	{
	public:
		typedef type* iterator;
		typedef const type* const_iterator;
        //...

	private:
		iterator _begin;
		iterator _end;
		iterator _end_storage;
	};
}

​

涉及到开辟和销毁空间,理应交给构造函数和析构函数来做。

		Vector()
		{
			_begin = nullptr;
			_end = nullptr;
			_end_storage = nullptr;
		}


		~Vector()
		{
			if (_begin)
			{
				delete[] _begin;
				_begin = nullptr;
				_end = nullptr;
				_end_storage = nullptr;
			}
		}

有时候想获取它的三个指针应该需要通过函数返回,不希望被修改则使用const保护。

还有的时候想要获取他的容量和数据个数也可以用函数返回

而想要初始化可以借助拷贝构造或者赋值来完成。

		const_iterator end_storage()const
		{
			return _end_storage;
		}

		const_iterator end()const
		{
			return _end;
		}

		const_iterator begin()const
		{
			return _begin;
		}

		size_t capacity()const
		{
			return _end_storage - _begin;
		}

		size_t size()const
		{
			return _end - _begin;
		}
		Vector(const Vector<type>& v)
		{
			_begin = new type[v.capacity()];
			//memcpy(_begin, v._begin, v.size());
			iterator it1 = _begin;
			iterator it2 = v._begin;
			while (it2 != v._end)
			{
				*it1 = *it2;
				it1++;
				it2++;
			}
			_end = _begin + v.size();
			_end_storage = _begin + v.capacity();
		}

		Vector<type>& operator=(Vector<type> v)
		{
			swap(v);
			return *this;
		}

这样的=操作符看起来较为简洁,但理解起来是,先传进来一个和实参一样的临时变量,然后swap交换this里面的内容和这个临时变量,最后返回Vector<type>类型的*this。并且这里也不用担心会有内存泄漏的问题,因为这里的v只是一个临时变量,出来函数以后会自动销毁。

函数内部用到的swap也非常简单,只需要将两个对象里面的私有成员交换即可。

		void Swap(Vector<type>& v)
		{
			std::swap(v._begin, _begin);
			std::swap(v._end, _end);
			std::swap(v._end_storage, _end_storage);
		}

vector和数组存储数据的方式一样,也是顺序存储,所以【】下标随机访问很容易实现

只是这里可以用一种只读和一种可读可写的方式重载此操作符。

		Vector<type>& operator[](size_t pos)
		{
			assert(pos < size());
			return _begin[pos];
		}

		const Vector<type>& operator[](size_t pos)const
		{
			assert(pos < size());
			return _begin[pos];
		}

插入数据首先要考虑扩容,库里面用的是reserve这个函数,参数使用的是新空间的大小。

reserve开辟空间时,开辟空间成功后,并不能简单的直接修改指针指向的位置,即使让_begin指向了新空间,那么_size和_capacity也能如我所愿指向新空间该指向的位置吗?不能这样做的原因是这样会导致迭代器失效,_begin指向新空间后,新的_size,_capacity需要通过_size(),_capacity()返回的偏移量来确定位置,而此时_begin已经指向了新空间,_size,_capacity还指向旧空间,试图用两个毫不相关的指针相减显然是荒谬的。所以一定先要把偏移量保存起来,_capacity的偏移量却无需保存,因为它的偏移量已经被形参表示了。

		void Pushback(int x)
		{
			if (capacity() == size())
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}

			*_end = x;
			_end++;
		}
		void reserve(size_t n)
		{
			if (n > size())
			{
				type* tmp = new type[n];
				if (tmp)
				{
					size_t sz = size();
					memcpy(tmp, _begin, sz*sizeof(type));
					
					delete[] _begin;
					_begin = tmp;
					_end = _begin + sz;
					_end_storage = _begin + n;
				}
			}
		}

插入数据和删除数据都需要挪动数据,插入数据仍需考虑扩容。

		void Insert(size_t pos, type val)
		{
			assert(pos <= size());
			if (capacity() == size())
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;
				reserve(newcapacity);
			}
			for (size_t i = size()-1; i >= pos; i--)
			{
				_begin[i+1] = _begin[i];
			}
			_begin[pos] = val;
			_end++;
		}
		type Erase(size_t pos)
		{
			assert(pos <= size());
			type del_val = _begin[pos];
			for (size_t i = pos+1; i < size(); i++)
			{
				_begin[i-1] = _begin[i];
			}
			_end--;
			return del_val;
		}

打印数据时有些地方必须用到库里面的东西,得要指明,在前面加上std::可以用迭代器也可以用范围for,但一般的容器都使用主流的迭代器。迭代器的力量不容小觑,在list等容器中也是主流。

		void Print()
		{
			iterator it=_begin;
			while (it != _end)
			{
				std::cout << *it<<' ';
				it++;
			}
			std::cout << std::endl;
		}

完整代码见Vector · c2e31cd · Admin/C++测试代码 - Gitee.com

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值