vector的使用和模拟实现:

 

 --------------血雨腥风,是对行远人的痛快的激励


目录

(1)Vector是什么?

 (2)Vector的功能使用:

①构造类:

② 容量空间:

resize:

reserve:

③modify:

 push_back\pop_back:

find\ insert;erase:

  (2)Vector的模拟实现:

 ①构造:

拷贝构造:

②modify:

push_back\reserve:

 resize/pop_back:

 ③补充:

insert\erase:

迭代器失效:

erase:

另:删除顺序表中的偶数

 (3)深层次深拷贝:


(1)Vector是什么?

vector是表示可变大小数组的序列容器。和数据结构的 顺序表尤为相似。

因为模板的介入,vector能存储的类型多样,且方便。


 (2)Vector的功能使用:

同上篇string(串)的功能 差不多,vector的基本功能颇为丰富:

①构造类:

(constructor) 构造函数声明
函数说明
vector()
无参构造
vector size_type n, const value_type& val = value_type()
构造并初始化 n val
vector (const vector& x); 
拷贝构造
vector (InputIterator fifirst, InputIterator last);
使用迭代器进行初始化构造

② 容量空间:

容量空间
函数说明
size有多少个有效数据
capacityvector的总容量
empty有没有数据
resize扩容,并初始化
reserve扩容

resize:

reserve:


③modify:
 

vector 增删查改
函数说明
push_back
尾插
pop_back
尾删
find
查找
insert
position 之前插入 val
erase
删除 position 位置的数据
swap
交换两个 vector 的数据空间
operator[]
像数组一样访问

 push_back\pop_back:

find\ insert;erase:

 注:erase 和 insert的 参数是迭代器>

insert:

erase:


  (2)Vector的模拟实现:

namespace dy
{
	//vector 是可以存储多种类型的顺序表
	template<class T>
	class vector 
	{
	   //vector的迭代器是个原生指针
		typedef T* iterator;
		typedef const T* const_iterator;

	public:

	private:
		iterator _start;
		iterator _finish;
		iterator _endofstorage;
	};
}

 ①构造:

	    //无参构造
		vector()
			: _start(nullptr),
			_finish(nullptr),
			_endofstorage(nullptr)
		{}

		//迭代器构造
		template<class templateiterator>
		vector(templateiterator first, templateiterator last) 
	        :_start(nullptr),
			_finish(nullptr),
			_endofstorage(nullptr)

		{
			while (first != last)
			{
				//复用尾插
				push_back(*first);  //push_back 之后会实现
				first++;
			}
		}

		//默认值初始化
		vector(size_t n, const T& val = T())  //因为缺省值不知道 具体给什么,所以就用匿名对象
			:_start(nullptr),
			_finish(nullptr),
			_endofstorage(nullptr)
		{
			reserve(n);  //开辟n个空间

			while (n--)
			{
				push_back(val);
			}
		}
      
            //析构
        	~vector()
		{
			delete[] _start;
 
			_start = _finish = _endofstorage = nullptr;
		}

capactiy、size、operator[] :
 

	size_t capacity() const
		{
			return _endofstorage-_start; //起始位置减去 容量位置
		}

		size_t size() const  //最好+上
		{
			return _finish-_start;  //起始位置减去 最后数字的位置
		}

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

拷贝构造:

 传统写法:

			//传统写法
        //拷贝构造
		//s2(s1)
		vector(const T& v)
			:_start(new T[v.capacity()]),  //开出同样大的空间
			_finish(nullptr),
			_endofstorage(nullptr)
		{
			//将s1 的数据拷贝给s2
			memcpy(_start, v._start, sizeof(T) * v.size());
			
			//更改_finish _endofstorage
			_finish = _start + v.size();
			_endofstorage = _start + v.capacity();
		}

	   //赋值运算符
		//s1=s2
		vector<T>& operator=(const vector<T>& v)  //operator(&s1,s2);
		{
			if (this != &v)  //自己和自己不赋值
			{
				delete[] _start; //先释放自己的空间
				_start= new T[v.capacity()];
				//拷贝数值
				memcpy(_start, v._start, sizeof(T) * v.size());

				_finish = _start + v.size();
				_endofstorage = _start + v.capacity();
			}
			return *this;
		}

上面是传统写法,代码较为冗杂,

现代写法:

	   //现代写法:
		void swap(const vector<T>& tmp)
		{
			//局部和全局分离
			::swap(_start, tmp._start);
			::swap(_finish,tmp._finish);
			::swap(_endofstorage, tmp._endofstorage);
		}

        //赋值
		vector<T>& operator=(const vector<T>& v)
		{
			if (*this != &v)
			{
              /*
            swap(_start, tmp._start);
			swap(_finish,tmp._finish);
			swap(_endofstorage, tmp._endofstorage);
                                               */
				//this->swap(tmp)
				swap(tmp)  //当然这三个swap实现的功能 一样 可以抽象出来
			}
			return *this;
		}

现代写法不仅兼顾了理解,且代码量少。


②modify:

push_back\reserve:

尾插框架很简单,就分是否需要扩容:

         

reserve的一大容易被忽略的点在这里。我们先看看测试:

		//迭代器
		iterator begin()
		{
			return _start;
		}

		iterator end()
		{
			return _finish;
		}

		const_iterator begin() const
		{
			return _start;
		}

		const_iterator end()const
		{
			return _finish;
		}

运行起来后程序直接崩了:

 那么问题出在哪里呢?

 所以,就要先保存差值:

        void reserve(size_t n)
 		{
			if (n > capacity())  //解耦
			{
				//新开 n 个空间
				T* tmp = new T[n];
				//避免找不到真正的size
				size_t sz = size();
				//可能_start为空
				if (_start)
				{
					memcpy(tmp, _start, sizeof(T) * sz);
					delete[] _start;
				}

				//更新
				_start = tmp;
				_finish = _start + sz;
				_endofstorage = _start + n;  //开辟了n个空间
			}
		}

		void push_back(const T& val)
		{
			//需要增容
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  //2倍扩容
				reserve(newcapacity);
			} 

			*_finish = val; //直接怼_finish 解引用找到最后一数的下一个位置
			_finish++;
		}

 

 resize/pop_back:

尾删较为简单也就多赘述

	bool empty()
		{
			return _finish = _start;
		}

		void pop_back()
		{
			assert(empty())
				--finish;
		}

	void resize(size_t n, const T& val=T())  //给定缺省值
		{
			//1.缩减
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				//统一是n>size()
				if (n > capacity()) //如果需要的空间大 那么再去申请空间
				{
					reserve(n);
				}

				while (_finish != _start + n)
				{
					*_finish = val;
					_finish++;
				}
			}

	     }


 ③补充:

insert\erase:

在pos之前的位置,插入(insert)数值:

我们可以借助,库里面find函数,查找pos位置:

void Insert(iterator pos, const T& val)
		{
			assert(pos < _finish);
			//扩容
			if (_finish == _endofstorage)
			{
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  //2倍扩容
				reserve(newcapacity);
			}

			//挪动pos之后的数据
			//从后往前
			iterator end = _finish - 1;
			while (end != pos)
			{
				*(end + 1) = *end;
				--end;
			}

			//pos位置处 放val
			*pos = val;
			_finish++;
		}

 从结果上来看,我们代码与我们预期相同。然而真的是这吗?

 此时我们换种情景:程序直接崩溃,而原因又在哪里呢?!

迭代器失效:

1.迭代器野指针:

我们不难猜到,问题最有可能出现的,就是在增容的部分,而恰恰第四次插入,就是需要增容~

 

所以,我们就需要对pos ,在扩容的时候,进行修正。 

 

我们让gap记录 start 与 pos之间的相对差距。 

void Insert(iterator pos, const T& val)
		{
			assert(pos < _finish);
			//扩容
			if (_finish == _endofstorage)
			{
				size_t gap = pos - _start;
				size_t newcapacity = capacity() == 0 ? 4 : capacity() * 2;  //2倍扩容
				reserve(newcapacity); 

				pos = _start + gap;
			}

			//挪动pos之后的数据
			//从后往前
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}

			//pos位置处 放val
			*pos = val;
			_finish++;
		}

2.迭代器代表的位置发生改变:

 插入数据不可避免,会改变原先pos位置所指向的值。所以在用这种迭代器时,需要加点心。

erase:

我们根据库里的来模拟,erase除了删除pos位置的数值,还要返回pos位置,下一个的地址。

	iterator erase(iterator pos)
		{
			//pos后的数 往前移动
			iterator begin = pos + 1;
			while (begin <= _finish)
			{
				*(begin - 1) = *begin;
				begin++;

			}
			_finish--;
			return pos;
		}

另:删除顺序表中的偶数

可能多数,认为删除偶数这样的逻辑是行得通的,比较也有结果。 

只要找到偶数,就删掉。 

换种数据就不行了。 

所以,每次it进行判断的时候,每删除一个,还需要进行判断。

	while (it != v1.end())
		{
			if (*it % 2 == 0)
			{
				it=v1.erase(it);  //利用erase的返回值 
			}
			else
			{
				it++;
			}
		}

 (3)深层次深拷贝:

大部分vector 重要的函数接口已经完成。

 但,这样的vector在存储自定义类,而非内置类型是,会出现问题。

例如:

此时我们插入四个值,还算正常。

 但当插入第五个数的时候。

 所以问题还是在于,扩容。

 ​​​

但为啥,当数据足够长的时候,会出现乱字呢?!

在linux下,string不管有多长,都会存在“buf”里面,

但是在vs下,做了别的处理,

当串字符足够小,就存在_buf里面 ,越长 则存在_ptr 

所以,这里用memcpy显然不合适,如果仅仅处理内置类型是足够的。

 所以和memcpy有关的,就需要更改:

         //拷贝构造
		//s2(s1)
		vector(const vector<T>& v)
			:_start(nullptr),  //开出同样大的空间
			_finish(nullptr),
			_endofstorage(nullptr)
		{
			reserve(v.capacity()); //进行增容
			for (auto e : v)
			{
				push_back(e);
			}
		
			 //传统写法
			//_start = new T[v.capacity()];
			将s1 的数据拷贝给s2
			memcpy(_start, v._start, sizeof(T) * v.size());
			//int sz = size();
			深层次考别
			//for (size_t i = 0;i < v.size();i++)!!
			//{
			//	_start[i] = v._start[i];
			//}

			更改_finish _endofstorage
			//_finish = _start + v.size();
			//_endofstorage = _start + v.capacity();
		}


	void reserve(size_t n)
		{
			if (n > capacity())  //解耦
			{
				//新开 n 个空间
				T* tmp = new T[n];
				//避免找不到真正的size
				size_t sz = size();
				//可能_start为空
				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;
				_endofstorage = _start + n;  //开辟了n个空间
			}
		}	

  vector也就告一段落啦!


祝你好运~

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值