STL中的vector

 

关于vector的介绍:

(1)vector是可变大小的序列容器。
(2)和数组一样支持随机访问
(3)容量的存在为的是留一些额外的空间以适应可能的增长
(4)尾上操作效率相对较高,但是其它位置操作相对较低

构造函数:
(1)vector() 无参构造
(2)vector(size_t n, const T& val=T()) //n个val
(3)vector(const vector& x) //拷贝构造
(4)vector(first, last) //迭代器区间

空间问题:
(1)size() //获取有效数据个数
(2)capacity() //获取容量大小
(3)empty() //判空
(4)resize(size_t n, T val=T()) 改变size
(5)reserve(size_t n) 改变capacity(这两个函数与string的对应规则一样)
需要注意的是:VS(PJ版本)下的capacity是按照1.5倍速度增长,LINUX下(SGI版本)按照2倍速度增长的

增删查改:
(1)push_back(const T& val)//尾插
(2)pop_back()//尾删
(3)insert(iterator pos, const T& val)//在pos位置插入一个数据(因为增容可能会引起迭代器失效)
(4)erase(iterator pos)//删除pos位置数据,返回下一个元素的迭代器(可能会引起迭代器失效)
(5)swap(vector& x)//交换两个vector的数据空间
(6)operator[]//重载[],向数组一样访问

迭代器失效问题
迭代器指的是迭代器指向的是一块不属于自己的内存,即迭代器对应的指针是“野指针”

vector中迭代器可能失效场景:
(1)所有可能增容的地方(如insert等),因为增容会导致开新空间,释放旧空间,当数据已经被拷贝到新空间后,旧空间已经被释放了,此时迭代器仍然指向的已经被释放的旧空间,这就是所谓的"迭代器失效"。
(2)erase删除一个元素后,导致原来元素的所有迭代器失效。但是erase()返回的是删除后下一个元素的迭代器

解决办法:每次都重新获取迭代器。

vector模拟实现:

#define _CRT_SECURE_NO_WARNINGS 1

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


//模拟实现vector

namespace VECTOR
{
	template <class T>//用模板类实现
	class Vector
	{
	public:
		//普通迭代器、const迭代器
		typedef T* iterator;
		typedef const T* const_iterator;

		//普通迭代器接口
		iterator begin()
		{
			return _start;
		}
		iterator end()
		{
			return _finish;
		}

		//const迭代器接口
		const_iterator begin()const
		{
			return _start;
		}
		const_iterator end()const
		{
			return _finish;
		}

		Vector()//无参构造函数
			:_start(nullptr)
			, _finish(nullptr)
			, _endofstorage(nullptr)
		{}

		~Vector()//析构函数
		{
			if (_start)
			{
				delete[] _start;
				_start = _finish = _endofstorage = nullptr;
			}
		}

		//拷贝构造函数
		//注意也不能用memcpy,对于内置类型可以,但是vector元素是string的话
		//就会出问题
		Vector(const Vector<T>& v)
		{
			_start = new T[v.Capacity()];

			for (size_t i = 0; i < v.Size(); i++)
			{
				_start[i] = v[i];
			}
			_finish = _start + v.Size();
			_endofstorage = _start + v.Capacity();
		}

		void Swap(Vector<T>& v)
		{
			swap(_start, v._start);
			swap(_finish, v._finish);
			swap(_endofstorage, v._endofstorage);
		}

		//赋值运算符重载(现代写法)
		Vector& operator=(Vector<T> v)
		{
			this->Swap(v);
			return *this;
		}

		//有效元素个数、有效容量
		const Size()const
		{
			return (_finish-_start);
		}
		const Capacity()const
		{
			return (_endofstorage-_start);
		}

		//重载[]
		T& operator[](size_t pos)
		{
			assert(pos < Size());
			return _start[pos];
		}
		const T& operator[](size_t pos)const
		{
			assert(pos < Size());
			return _start[pos];
		}

		//改变容量
		//这种非常容易出错的一个接口,要注意以下几点:
		//(1)当需要扩容时,拷数据不能简单的用memcpy,因为memcpy是内存的"浅拷贝",
		//如果vector中的元素是string对象时,会使得新空间中的与旧空间对象(_str)
		//指向同一块空间,当旧空间释放时,新空间的_str会成为野指针,最后释放
		//两次也会导致问题。因此要使用赋值(调用string赋值时的深拷贝)
		//(2)扩容前将原来的size值保存下来,因为size的计算依赖于_finish和_start,
		//当_start改变后,再利用_finish和_start计算size势必会出现问题
		//(3)当原空间元素存在时不为空时才拷数据()否则会相当于对空指针左解引用
		void reserve(size_t n)
		{
			//只有n大于原容量时会去扩容,否则不理睬
			if (n > Capacity())
			{
				size_t size = Size();//获取原来的size
				T* tmp = new T[n];

				if (size > 0)//有元素才拷
				{
					for (size_t i = 0; i < size; i++)
					{
						//如果vector中元素是string对象,这里会
						//调用string的赋值运算符重载(进行深拷贝)
						tmp[i] = _start[i];
					}
					delete[] _start;
				}

				_start = tmp;
				_finish = _start + size;
				_endofstorage = _start + n;
			}
		}

		//改变有效元素个数(多余的默认用value填充)
		void resize(size_t n, const T& value = T())
		{
			if (n > Capacity())//需要增容
			{
				reserve(n);
			}

			if (n < Size())//直接将元素个数减少(该_finish)
			{
				_finish = _start + n;
			}
			else//需要填充
			{
				while (_finish < _start + n)
				{
					*_finish = value;
					++_finish;
				}
			}
		}


		//在pos位置前插入一个元素(位置传的是迭代器)
		//这个接口也要注意一个问题“迭代器失效”,因此
		//将pos位置先得保存一下,然后reserve之后需要重置一下pos
		void Insert(iterator pos, const T& x)
		{
			size_t n = pos - _start;//保存pos到其实距离

			if (_finish == _endofstorage)//需要增容
			{
				size_t newCapacity = (Capacity() == 0 ? 2 : Capacity() * 2);
				reserve(newCapacity);
			}

			pos = _start + n;//将pos重置到新空间的对应位置

			//挪数据
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *(end);
				end--;
			}

			//将这个数据插入到pos位置
			*pos = x;
			++_finish;
		}

		//删除pos位置元素
		void Erase(iterator pos)
		{
			assert(pos < _finish && pos >= _start);

			iterator cur = pos + 1;//cur指向pos下一个元素
			while (cur != _finish)
			{
				*(cur - 1) = *cur;
				++cur;
			}
			--_finish;
		}


	private:
		T* _start;//指向首元素
		T* _finish;//指向尾元素的下一个位置
		T* _endofstorage;//指向可用有效空间
	};

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值