STL--vector

一、vector介绍

vector是表示大小可以更改的数组的序列容器

就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。

本质上,vector使用动态分配的数组来存储其元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。如果每次插入新元素都重新开辟一块空间,并且拷贝数据,将会消耗很多的资源。因此每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。

vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间更大。

优点缺点
支持下标随机访问

1.头部中部插入删除效率低下

2.扩容问题

二、vector接口

详情可见vector - C++ Reference (cplusplus.com)icon-default.png?t=N7T8https://legacy.cplusplus.com/reference/vector/vector/

1.vector常见构造

函数名称功能说明
vector ()无参构造
vector (size_type n, const value_type& val = value_type())构造并初始化n个val
vector (const vector& x)拷贝构造
vector (InputIterator first, InputIterator last)使用迭代器进行初始化构造
void test_2()
{
	vector<int> v1(10, 1);//默认参数构造
	vector<int> v2(v1.begin(), v1.end());
	for (auto w : v2)
	{
		cout << w << " ";
	}
	cout << endl;


	string s1("hello world");
	vector<char> v3(s1.begin(), s1.end());//迭代器构造
	for (auto ch : v3)
	{
		cout << ch << " ";
	}
	cout << endl;
}

2.vector容量操作

函数名称功能说明
size获取数据个数
capacity获取容量大小
empty判断是否为空
reserve改变vector的capacity
resize改变vector的size
void test_4()
{
	//如果 n 小于当前容器大小
	//则内容将减少到其前 n 个元素,删除超出的元素(并销毁它们)

	//如果 n 大于当前容器大小
	//则通过在末尾插入所需数量的元素来扩展内容,以达到 n 的大小。如果指定了 val,则新元素将初始化为 val 的副本,否则,它们将被值初始化。

	//如果 n 也大于当前容器容量
	//则会自动重新分配分配的存储空间
	vector<int> v1;
	cout << v1.size() << endl;
	v1.resize(10, 0);
	cout << v1.size() << endl;
	vector<int> v2(10, 0);
	cout << v2.size() << endl;

	cout << v2.capacity() << endl;
	v2.reserve(15);
	cout << v2.capacity() << endl;
}

3.vector访问与遍历

函数名称功能说明
operator[]像数组一样访问
begin+ end获取第一个数据位置的iterator/const_iterator, 获取最后一个数据的下一个位置的iterator/const_iterator
rbegin + rend获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的 reverse_iterator
范围forC++11支持更简洁的范围for的新遍历方式
void test_3()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(3);
	v1.push_back(5);
	v1.push_back(7);
	v1.push_back(9);

	vector<int>::iterator it = v1.begin();
	while (it != v1.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;


	auto rit = v1.rbegin();
	while (rit != v1.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;
}

4.vector修改操作

函数名称功能说明
push_back尾插
pop_back尾删
find查找(注意这个是算法模块实现,不是vector的成员接口)
insert在position之前插入val
erase删除position位置的数据
swap交换两个vector的数据空间
void test_5()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(3);
	v1.push_back(5);
	v1.push_back(7);
	v1.push_back(9);

	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;

	vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);
	if (pos != v1.end())
	{
		v1.insert(pos, 20);
	}

	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;

	pos = find(v1.begin(), v1.end(), 3);
	if (pos != v1.end())
	{
		v1.erase(pos);
	}

	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;

	v1.erase(v1.begin());
	for (auto ch : v1)
	{
		cout << ch << " ";
	}
	cout << endl;
}

5.vector迭代器失效问题

迭代器的主要作用就是让算法能够不用关心底层数据结构,其底层实际就是一个指针,或者是对指针进行了 封装,比如:vector的迭代器就是原生态指针T* 。因此迭代器失效,实际就是迭代器底层对应指针所指向的空间被销毁了,而使用一块已经被释放的空间,造成的后果是程序崩溃(即如果继续使用已经失效的迭代器, 程序可能会崩溃)

vector迭代器失效的操作包括:①引起底层空间改变的操作(resize、reserve、insert、assign、push_back)②指定位置元素的删除操作(erase)

void test_1()
{
	vector<int> s1(10, 10);
	for (auto ch : s1)
	{
		cout << ch << " ";
	}
	cout << endl;
	vector<int>::iterator it = s1.begin();
	while (it != s1.end())
	{
		cout << *it<<" ";
		it++;
	}
	s1.push_back(66);
	s1.push_back(555);
	s1.push_back(4444);
	cout << *it << endl;//注意在push_back之后不可以再调用之前的迭代器
	//因为迭代器的指向位置改变了
	cout << endl;
	vector<int>::reverse_iterator rit = s1.rbegin();
	while (rit != s1.rend())
	{
		cout << *it << " ";//这里会导致迭代器失效
		it++;
	}
	cout << endl;

	for (auto e : s1)
	{
		cout << e << " ";
	}
	cout << endl;
}

实际上string也会出现迭代器失效问题,原因与vector类似。但是string一般进行增删查改采用下标模式而不是迭代器。

三、vector模拟实现

#pragma once

#include<iostream>
#include<assert.h>

using std::cout;
using std::endl;
using std::find;
namespace my_vector
{
	template<class T>
	class vector
	{
	public:
		typedef T* iterator;
		typedef const T* const_iterator;

		vector()
			:_start(nullptr),_finish(nullptr),_end_of_storage(nullptr)
		{}
		vector(size_t n, const T& x = T())
			:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
			//T()是一个匿名对象,采用引用使其生命周期延长至与x相同
			//匿名对象,临时对象都具有常性,所以需要const
		{
			reserve(n);
			for (size_t i = 0; i < n; i++)
			{
				push_back(x);
			}
		}
		vector(int n, const T& x = T())//进行重载避免与迭代器构造产生非法的间接寻址问题
			:_start(nullptr), _finish(nullptr), _end_of_storage(nullptr)
		{
			reserve(n);
			for (int i = 0; i < n; i++)
			{
				push_back(x);
			}
		}
		vector(const vector<T>& v)//拷贝构造
		{
			_start = new T[v.capacity()];
			//memcpy(_start,v._start,sizeof(T)*v.size())无法处理自定义类型
			for (size_t i = 0; i < v.size(); i++)
			{
				_start[i] = v._start[i];
			}
			_finish = _start + v.size();
			_end_of_storage = _start + v.capacity();
		}
		template<class InputIterator>
		vector(InputIterator first, InputIterator last)//迭代器构造
		{
			while (first != last)//不能保证last的地址就大于first,所以要采用不等于
			{
				push_back(*first);//可能会报错非法的间接寻址
				first++;
			}
		}
		~vector()
		{
			delete[] _start;
			_start = nullptr;
			_finish = nullptr;
			_end_of_storage = nullptr;//实际有效存储的最后位置
		}


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


		//运算符重构
		T& operator[](size_t pos)
		{
			assert(pos < size());
			return _start[pos];
		}
		const T& operator[](size_t pos)const
		{
			assert(pos < size());
			return _start[pos];
		}


		//增删查改
		void push_back(const T& x)//尾插
		{
			if (_finish == _end_of_storage)
			{
				reserve(capacity() == 0 ? 4 : 2 * capacity());
			}
			*_finish = x;
			++_finish;
		}
		void pop_back()//尾删
		{
			assert(!empty());
			--_finish;
		}
		//size_t find(const T& x)//查找
		//{
		//	iterator head = _start;
		//	int pos = 0;
		//	while (head != _finish)
		//	{
		//		if (x == *head)
		//		{
		//			return pos;
		//		}
		//		head++;
		//		pos++;
		//	}
		//	return -1;
		//}
		iterator erase(iterator pos)//删除pos,并返回删除元素的下一个位置
		{
			assert(pos >= _start);
			assert(pos <_finish);
			iterator start = pos + 1;
			while (start != _finish)
			{
				*(start - 1) = *start;
				++start;
			}
			--_finish;
			return pos;
		}
		iterator insert(iterator pos, const T& x)
		{
			assert(pos >= _start);
			assert(pos <= _finish);
			
			if (_finish==_end_of_storage)
			{
				size_t len = pos - _start;
				reserve(capacity() == 0 ? 4 : 2 * capacity());
				pos = _start + len;//扩容后空间为新地址,需要更新pos位置
			}
			iterator end = _finish - 1;
			while (end >= pos)
			{
				*(end + 1) = *end;
				--end;
			}
			*pos = x;
			++_finish;

			return pos;
		}


		//容量操作
		void reserve(size_t n)//扩容
		{
			if (n > capacity())
			{
				T* tmp = new T[n];
				size_t sz = size();
				if (_start)
				{
					//memcpy(tmp, _start, sizeof(T) * size());对于自定义类型容易出现浅拷贝问题
					for (size_t i = 0; i < sz; i++)
					{
						tmp[i] = _start[i];
					}
					delete[] _start;
				}
				_start = tmp;
				_finish = _start + sz;
				_end_of_storage = _start + n;
			}
		}
		void resize(size_t n,T val=T())
		//这里的val为匿名默认构造,从而适应自定义类型
		{
			if (n < size())
			{
				_finish = _start + n;
			}
			else
			{
				if (n > capacity())
				{
					reserve(n);
				}
				while (_finish != _start + n)
				{
					*_finish = val;
					++_finish;
				}
			}
		}
		size_t capacity()const//容量
		{
			return _end_of_storage - _start;
		}
		size_t size()const
		{
			return _finish - _start;
		}
		bool empty()
		{
			return _start == _finish;
		}
	private:
		iterator _start;
		iterator _finish;//指向有效元素的下一个位置,也就是整个空间的末尾
		iterator _end_of_storage;//指向有效空间的尾部
	};


	void test_1()
	{
		vector<int> v1;
		v1.push_back(5);
		v1.push_back(4);
		v1.push_back(3);
		v1.push_back(2);
		v1.push_back(1);

		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		//cout <<find(7) << endl;//自主实现find测试
	}

	void test_2()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);
		v1.push_back(5);


		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		v1.pop_back();
		v1.pop_back();

		vector<int>::iterator it = v1.begin();
		while (it != v1.end())
		{
			cout << *it << " ";
			++it;
		}
		cout << endl;

		v1.pop_back();
		v1.pop_back();
		v1.pop_back();
		//v1.pop_back();

		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

	}

	void test_3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		auto pos = find(v1.begin(), v1.end(), 3);
		if (pos != v1.end())
		{
			v1.insert(pos, 30);
		}

		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		(*pos)++;
		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;
	}

	void test_4()
	{
		vector<int> v1(10, 5);
		for (auto e : v1)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<int> v2(v1);
		for (auto e : v2)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<std::string> v3(3, "111111111111111111111");
		for (auto e : v3)
		{
			cout << e << " ";
		}
		cout << endl;

		vector<std::string> v4(v3);
		for (auto e : v4)
		{
			cout << e << " ";
		}
		cout << endl;

		v4.push_back("2222222222222222222");
		v4.push_back("2222222222222222222");
		v4.push_back("2222222222222222222");
		for (auto e : v4)
		{
			cout << e << " ";
		}
		cout << endl;

	}
}

 erase操作思路

由于erase是对vector的修改,因此可能会产生迭代器失效问题,解决方法与下面的insert类似。

erase在不同的编译器下报错并不同,但是应当默认eraser之后不要访问pos,pos已经失效

insert操作思路

以下面这段代码为例,find函数返回一个迭代器,如果要对pos位置进行更改可能会出现前面所提及的迭代器指向失效问题

但由于vector中的begin()和end()函数是传值返回,所以在insert的实现中,参数pos不能采用引用传参

因此STL规定通过给insert函数设置返回值的方式来获取pos

	void test_3()
	{
		vector<int> v1;
		v1.push_back(1);
		v1.push_back(2);
		v1.push_back(3);
		v1.push_back(4);

		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		auto pos = find(v1.begin(), v1.end(), 3);
		if (pos != v1.end())
		{
			v1.insert(pos, 30);
		}

		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;

		(*pos)++;//pos会失效,不能使用
		for (size_t i = 0; i < v1.size(); ++i)
		{
			cout << v1[i] << " ";
		}
		cout << endl;
	}

四、vector嵌套

假设有对象vector<vector<int>>  v1 ,这个v1是一个vector容器,其中存储的每一个元素都是一个包含int类型的vector容器。如果使用memcpy函数的拷贝构造,会造成内部存放的vector容器指向的地址相同,在析构时会重复析构二次释放

所以需要采用赋值拷贝,实现深拷贝

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值