一.vector介绍

  1. vector是一个多功能的,能够操作多种数据结构和算法的模板类和函数库。简单说,就是一个可以存放任意类型的动态数组,能够增加和压缩数据。

  2. vector也同string一样,采用连续的存储空间来存储数据,这也就意味着,可以使用[]下标的方式访问其中数据,可读性较强

  3. vector是动态数组,它并不需要事先规定好容量,而是在插入数据时,根据当前容量是否足够而选择是否扩容。不同平台的STL库的实现方式不一定一样,导致不同平台扩容的大小不同

  4. 同时,因为是连续的存储空间,这就代表了vector的尾插,尾删,访问都非常的高效,但是在中间插入和删除元素,因为需要挪动数据,所以相对于list,deque等链式存储效率就低了

二.构造函数

构造函数声明接口说明
vector()无参构造(全缺省)
vector(size_type n,const value_type&val=value_type)构造并用n个val初始化
vector(const vector&x)拷贝构造
vector(InputIterator first,InputIterator last)迭代器模板作参数构造

在这里插入图片描述

size_type是无符号整型,value_type是模板,用来接收任意数据类型。
第一个无参的const allocator_type&alloc是一个内存池,当vector需要申请空间的时候,会优先去内存池申请,加快了分配空间的效率

在学习了string之后,这部分可以说是轻车熟路了。
测试代码如下:

//构造函数
void vector_test1()
{
	vector<int> v1;//无参构造(全缺省)
	vector<int> v2(6, 6);//构造并n个val初始化
	vector<int> v3(v2);//拷贝构造
	vector<int> v4(v3.begin(), v3.end());//迭代器区间构造

}

在这里插入图片描述

vector<>尖括号中的是指定类型,因为vector是适配任意数据类型的,本质是使用了模板,所以需要在定义时,指定要使用的数据类型

三.三种遍历

因为vector本质是动态数组,所以支持和数组,string一样的[]下标访问。
同时容器都可以使用迭代器区间来访问
迭代器可以访问,那么套用迭代器的范围for同样可以使用

1.[]重载

在这里插入图片描述

[]重载支持可读可写——第一种
同时也支持只读——第二种

代码如下:

可读可写

void vector_test2()
{
	//[]重载
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);
	v1.push_back(3);
	v1.push_back(4);

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

在这里插入图片描述

只读

void constPrint(const vector<int>&v)
{
	for (int i = 0; i < v.size(); i++)
	{
		cout << v[i] << " ";
	}
	cout << endl;
}

2.迭代器

vector的迭代器同样有正向反向,const和非const的

以下演示正反向

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

	vector<int>::reverse_iterator rit = v1.rbegin();
	while (rit != v1.rend())
	{
		cout << *rit << " ";
		rit++;
	}
	cout << endl;

在这里插入图片描述

迭代器指向位置如下图
在这里插入图片描述

3.范围for

因为范围for底层是迭代器,所以也可以使用。但是范围for只能正向走,不能反向访问

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

在这里插入图片描述

四.容量操作

在这里插入图片描述

接口函数接口说明
size返回大小
capacity返回容量
max_size返回可构建的最大大小
resize扩容并初始化
reserve扩容
shrink_to_fit把capacity缩小到和size一样
empty判空

同string一样,vector也提供了size,capacity等容量操作的接口
size是返回当前vector的大小,capacity是返回当前vectr的容量
max_size实际意义不大
主要关注resize和reserve还有shrink_to_fit

reserve就是扩容,只改变capacity的大小
resize是在可能扩容的基础上,同时改变size,并初始化
如果给定的值小于原本的容量,则不会有所改变
因为缩容的代价很大,需要申请新空间,拷贝数据,指向新空间,释放旧空间
所以一般不缩容

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

	cout << "v1.size =" << v1.size() << endl;
	cout << "v1.capacity =" << v1.capacity() << endl;

	v1.reserve(30);
	cout << "v1.size =" << v1.size() << endl;
	cout << "v1.capacity =" << v1.capacity() << endl;

	v1.resize(20);
	cout << "v1.size =" << v1.size() << endl;
	cout << "v1.capacity =" << v1.capacity() << endl;

	v1.resize(35);
	cout << "v1.size =" << v1.size() << endl;
	cout << "v1.capacity =" << v1.capacity() << endl;

	v1.shrink_to_fit();
	cout << "v1.size =" << v1.size() << endl;
	cout << "v1.capacity =" << v1.capacity() << endl;
}

在这里插入图片描述

扩容规则

因为不同平台使用了不同版本的STL,虽然各个容器的性质,功能接口大抵相同,但是具体实现仍会有所不同
这里,vs下的扩容倍数和Linux下的扩容倍数就有所不同

void vector_test3()
{
	vector<int> v1;

	size_t cap = v1.capacity();
	for (int i = 0; i < 100; i++)
	{
		v1.push_back(i);
		if (cap != v1.capacity())
		{
			cout << "new capacity:" << v1.capacity() << endl;
			cap = v1.capacity();
		}
	}
}

在这里插入图片描述

vs下,扩容是1.5倍扩容

在这里插入图片描述

在这里插入图片描述

Linux下,扩容是2倍扩

五.增删查改

在这里插入图片描述

接口函数功能说明
assign新内容覆盖旧内容并调整size
push_back尾插
pop_back尾删
insert任意位置插入
erase任意位置删除
swap和另一个vector交换数据
clear清空数据
  • assign在这里插入图片描述
    第一种. 使用n个val覆盖
void vector_test4()
{
	vector<int> v1 = { 1,2,3,4,5,6,7 };
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;
	cout << "size=" << v1.size() << endl;

	v1.assign(3, 10);
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;
	cout << "size=" << v1.size() << endl;
}

在这里插入图片描述
第二种. 用迭代器区间覆盖

void vector_test4()
{
	vector<int> v1 = { 1,2,3,4,5,6,7 };
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;
	cout << "size = " << v1.size() << endl;

	vector<int> v2 = { 3,3,3,3,3 };

	v1.assign(v2.begin(), v2.end());
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;
	cout << "size = " << v1.size() << endl;
}

在这里插入图片描述

  • find
    在这里插入图片描述

在vector的函数里,我们并没有看到find,这是因为find设计成了一个函数模板,可以适配多种STL容器的查找>功能,所以没有再封装在某个单独的容器里。
注意,此时的find返回值是迭代器,不再是下标了,因为要适配之后学的list,deque等

六. 迭代器失效

我们先看一看小场景

void vector_test5()
{
	vector<int> v1 = { 1,2,3,4,5,6,7 };
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;

	auto pos = find(v1.begin(), v1.end(), 3);
	v1.insert(pos, 666);

	(*pos)++;
	for (auto i : v1)
	{
		cout << i << " ";
	}
	cout << endl;
}

此处我们先找到3的位置,并且用迭代器pos接收,再在pos位置插入666
在这里插入图片描述
但是后续的(*pos)++却报错了
这是为什么呢?

因为我们vector是类似数组那样,一旦有数据进入,则可能出现扩容,一旦扩容,会重新开辟新空间,拷贝数据,释放旧空间
而pos迭代器所指向的原先空间就失效了,pos迭代器就失效了,这就是迭代器失效

同时,erase也会有迭代器失效

虽然erase删除数据,按理来说不会出现扩容现象,也就不会有新空间,但是为了防止非法访问(删除最后一个数据,但却仍通过外部迭代器访问),vs编译器统一不允许再次使用原先的迭代器。

七.结束语

vector的使用暂时学到这,迭代器后续还会有更深入的学习,感谢收看。本章用于记笔记,如果有不对或者不足的地方,欢迎大佬们指正,补充。感谢大家的阅读,如果感觉博主写的还可以,麻烦点个赞支持一下,阿里嘎多。拜托了,这对我真的很重要~
在这里插入图片描述

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐