【C++初阶】第11课—vector

1. 认识vector

  • vector是向量、矢量的意思
  • vector其实就是数据结构阶段学过的顺序表,行为看起来像指针一样的容器,底层不一定是用指针实现的,具体根据编译器的底层实现结构为准
  • vector的使用:vector<数据类型> 对象名

在这里插入图片描述


2. vector的遍历

在这里插入图片描述


3. vector的构造

在这里插入图片描述


  • 对于vector的析构,跟string类似,它会自动调用

4. vector常用的接口

在这里插入图片描述


  • vector上述的接口与string的接口并无二异

在这里插入图片描述


  • cbegin( )和cend( )的用法与begin和end类似,无非就是常量迭代器不能改变数据而已

5. vector的容量

在这里插入图片描述


在这里插入图片描述


  • 对于判空empty和请求缩容shrink_to_fit不再过多赘述,string里面都有讲到

6. vector的元素访问

在这里插入图片描述


在这里插入图片描述


  • at和下标访问操作符[ ]都是用来访问vector内的元素,区别是越界时下标操作符[ ]会报错,而at会抛出异常,关于这点string已经讲过,后续讲到捕获异常时可重温复习以加深理解

7. vector的修改

在这里插入图片描述


在这里插入图片描述


  • 其余的像swap用来交换两个vector对象,使用时可参考官方文档,大部分与string类模版中的用法类似

8. vector<vector<int>>的使用

  • vector<vector<int>>其实就是类似二维数组的用法,使用vector实例化出int类型的对象,再使用vector<vector<int>>实例化出vector<int>的对象

在这里插入图片描述


在这里插入图片描述


#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
//vector<vector<int>>的使用(杨辉三角)
size_t numRows = 0;
cin >> numRows; //行数
vector<vector<int>> vv(numRows);
//所有数设置为1
for (size_t i = 0; i < numRows; i++)
{
	vv[i].resize(i + 1, 1);
}
//中间元素为上面两个元素之和
for (size_t i = 2; i < numRows; i++)
{
	for (size_t j = 1; j < vv[i].size() - 1; j++)
	{
		vv[i][j] = vv[i - 1][j] + vv[i - 1][j - 1];
	}
}
//打印
for (size_t i = 0; i < numRows; i++)
{
	//打印前面的空格
	for (size_t k = 0; k < numRows - i - 1; k++)
	{
		cout << " ";
	}
	for (size_t j = 0; j < vv[i].size(); j++)
	{
		cout << vv[i][j] << " ";
	}
	cout << endl;
}
return 0;
}

9. vector的使用

  • 对于vector容器,也可以使用string类型,其插入string类型的数据如下

在这里插入图片描述


  • 关于vector和C++算法库的一些使用,以下先作为一个了解

在这里插入图片描述


10. 模拟实现vector

  • 之前讲述过,迭代器都是左闭右开的区间,对于begin指向第一个数,而end则指向最后一个数的下个位置
  • 因此对于vector的成员变量_start和_finish、_end_of_storage来讲,_finish就是指向有效数据的下个位置,_end_of_storage就指向容量的下个位置

在这里插入图片描述


在这里插入图片描述


  • 接下主要讲reserve扩容的坑,其余较容易实现

在这里插入图片描述


  • insert在pos位置插入字符
  • 注意:pos的类型是迭代器iterator,因为vector的成员变量是iterator类型,类似迭代器begin()和end()

在这里插入图片描述


  • 对于上述insert插入数据的代码,存在一个巨大隐患:迭代器失效的问题,接下来主要讲容器中的迭代器失效的问题

11. 迭代器失效

11.1 insert插入数据内部迭代器失效

在这里插入图片描述


  • 解决上面迭代器失效的方法:扩容后更新pos

在这里插入图片描述


11.2 insert插入数据外部迭代器失效

在这里插入图片描述


  • 即使是库里面实现的vector,对于insert插入数据也会出现迭代器失效的问题
  • 迭代器失效的根本原因就是扩容,扩容前后外部迭代器指向的是旧空间,而扩容后旧空间被释放,再访问就会报错
  • 因此对于insert插入数据后外部传参的迭代器,由于不同的平台结果可能不同,统一认为该迭代器失效
  • 解决办法:insert插入数据后更新迭代器

在这里插入图片描述


11.3 erase删除数据迭代器失效

在这里插入图片描述


在这里插入图片描述


  • 解决办法:在erase删除数据后即使更新迭代器it

在这里插入图片描述


  • 总结
  • 对于迭代器失效的问题,主要存在于容器插入和删除元素时,这里以vector为例,在insert插入数据和erase删除数据后,迭代器就处于失效的状态,这是因为插入数据扩容导致以及删除数据缩容导致的一系列问题
  • 对于不同的编译器,结果可能不同,并且vs对于迭代器失效的检查尤为严格,它会对失效的迭代器进行标记,如果尝试使用这些失效的迭代器,它就会报错
  • 因此统一认为容器插入数据和删除数据后迭代器处于失效的状态
  • 如果想继续使用失效的迭代器,解决办法就是在插入数据或删除数据前后根据实际情况及时更新迭代器,使迭代器正确指向对应的数据

12. 模拟实现resize

在这里插入图片描述


在这里插入图片描述


13. 模拟实现vector的拷贝构造

在这里插入图片描述


14. 模拟实现vector的赋值操作符重载

在这里插入图片描述


15. 模拟实现reserve存在的坑

  • reserve扩容的第一个坑

在这里插入图片描述


  • reserve扩容的第二个坑

在这里插入图片描述

  • 插入前四个字符串时没有问题,但是插入第5个字符串时,为什么会出现问题呢?
  • 因为插入第5个字符串时会扩容,reserve扩容中的memcpy其实就是一个浅拷贝,之前再C语言阶段实现过memcpy这个函数,它是一个字节一个字节拷贝的
  • 参考链接:memcpy的使用和模拟实现

在这里插入图片描述


在这里插入图片描述


  • 解决方案

在这里插入图片描述


16. 模拟实现vector初始化

  • C++11提供了vector初始化列表来进行初始化

在这里插入图片描述


  • inltializer_list是C++11设置一个模版类型
  • 其用法和之前创建数组有点类似

在这里插入图片描述


在这里插入图片描述


  • C++还提供了迭代器区间初始化

在这里插入图片描述


在这里插入图片描述


  • 当然数组也可以当做迭代器使用

在这里插入图片描述


17. vector构造时容易出现的坑

  • 用n个val值构造vector对象

在这里插入图片描述


在这里插入图片描述


  • 解决措施

在这里插入图片描述


18. 模拟实现vector代码

//模版
template <class T>
class vector
{
public:
	//迭代器
	typedef T* iterator;
	typedef const T* const_iterator;

	//构造函数
	vector()
	{}

	//初始化列表初始化
	vector(initializer_list<T> il)
	{
		reserve(il.size());
		for (auto& e : il)
		{
			push_back(e);
		}
		cout << "初始化列表初始化:" << endl;
	}

	//迭代器区间初始化
	//类模板函数的成员函数,也可以是函数模版
	template <class InputIterator>
	vector(InputIterator first, InputIterator last)
	{
		while (first != last)
		{
			push_back(*first);
			++first;
		}
	}

	//vector的构造--->n个val
	vector(int n, const T& val = T())
	{
		resize(n, val);
	}

	vector(size_t n, const T& val = T())
	{
		resize(n, val);
	}

	//拷贝构造 
	vector(const vector<T>& v)
	{
		reserve(v.size());
		for (auto& e : v)
		{
			//this->push_back(e);
			push_back(e);
		}
	}

	//有效数据size
	size_t size() const
	{
		return _finish - _start;
	}

	//容量
	size_t capacity() const
	{
		return _end_of_storage - _start;
	}

	//begin
	iterator begin()
	{
		return _start;
	}

	//end
	iterator end()
	{
		return _finish;
	}

	//const begin
	const_iterator begin() const
	{
		return _start;
	}

	//const end
	const_iterator end() const
	{
		return _finish;
	}

	//resize改变有效数据个数
	void resize(size_t n, const T& val = T())
	{
		if (n < size())
		{
			_finish = _start + n;
		}
		else
		{
			reserve(n);
			while (_finish != _start + n)
			{
				*_finish = val;
				_finish++;
			}
		}
	}

	//扩容
	void reserve(size_t n)
	{
		if (n > capacity())
		{
			size_t oldsize = size();
			T* tmp = new T[n];
			/*memcpy(tmp, _start, sizeof(T) * size());*/

			for (size_t i = 0; i < oldsize; i++)
			{
				tmp[i] = _start[i];
			}
			delete[] _start;
			_start = tmp;
			_finish = oldsize + _start;
			_end_of_storage = n + _start;
		}
	}

	//尾插
	void push_back(const T& x)
	{
		if (_finish == _end_of_storage)
		{
			reserve(capacity() == 0 ? 4 : 2 * capacity());
		}
		*_finish = x;
		_finish++;
	}

	//尾删
	void pop_back()
	{
		assert(_finish > _start);
		_finish--;
	}

	//重载下标操作符[]
	T& operator[](size_t i)
	{
		return _start[i];
	}

	//赋值操作符---第一种写法
	//vector<T>& operator=(const vector<T>& v)
	//{
	//	if (this != &v)
	//	{
	//		delete[] _start;
	//		_start = _finish = _end_of_storage = nullptr;
	//		reserve(v.size());
	//		for (auto& e : v)
	//		{
	//			//this->push_back(e);
	//			push_back(e);
	//		}
	//	}
	//	return *this;
	//}


	void swap(vector<T> v)
	{
		std::swap(_start, v._start);
		std::swap(_finish, v._finish);
		std::swap(_end_of_storage, v._end_of_storage);
	}
	//赋值操作符---第二种写法
	vector<T>& operator=(const vector<T>& v)
	{
		swap(v);
		return *this;
	}

	//任意位置插入,insert插入数据后迭代器失效
	void 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;
		}
		//挪动数据
		iterator end = _finish - 1;
		while (end >= pos)
		{
			*(end + 1) = *end;
			--end;
		}
		//插入数据
		*pos = x;
		++_finish;
	}

	//erase删除数据
	iterator erase(iterator pos)
	{
		assert(pos >= _start);
		assert(pos < _finish);

		iterator begin = pos + 1;
		while (begin < _finish)
		{
			*(begin - 1) = *begin;
			++begin;
		}
		--_finish;

		return pos;
	}

	//析构
	~vector()
	{
		delete[] _start;
		_start = _finish = _end_of_storage = nullptr;
	}
private:
	iterator _start = nullptr;
	iterator _finish = nullptr;
	iterator _end_of_storage = nullptr;
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值