序列式容器Vector详细介绍

一、 头文件

vector 包含在 <vector> 中
在使用时需要包含头文件

#include<vector>

二、 vector 简介

在cplusplus里面的vector的简介
在这里插入图片描述
vector是表示可以改变大小的数组的序列容器。
在这里插入图片描述
这里对vector还有很多的介绍解释,这里就不再一一的翻译了,对它主要的进行了解就可以了,

1. vector是表示可变大小数组的序列容器。 
2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。
但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。 
3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小 为了增加存储空间。
其做法是分配一个新的数组,然后将全部元素移到这个数组。一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。 
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存 储空间更大。
不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是 对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。 
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。 
6. 与其它动态序列容器相比(deques, lists and forward_lists),vector在访问元素的时候更加高效,在 末尾添加和删除元素相对高效。
对于其它不在末尾的删除和插入操作,效率更低。比起lists和 forward_lists统一的迭代器和引用更好。 
vector 的属性
序列容器中的元素按严格的线性顺序排列。各个元素按其在此顺序中的位置进行访问。**
允许直接访问序列中的任何元素,甚至通过指针算法,并在序列末尾提供相对快速的元素添加/删除。
容器使用分配器对象动态处理其存储需求。

三、vector 的定义

说了那么多大家可能还是不太理解,在c++ prime这本书里,介绍到:
vector 是同一种类型的对象的集合,每个对象都有一个对应的整数索引值。 和 string 对象一样,标准库将负责管理与存储元素相关的内存。我们把 vector 称为容器,是因为它可以包含其他对象。一个容器中的所有对象都必须是同一种 类型的。

vector 是一个类模板(class template)。使用模板可以编写一个类定义 或函数定义,而用于多个不同的数据类型。因此,我们可以定义保存 string 对 象的 vector,或保存 int 值的 vector,又或是保存自定义的类类型对象(如 Sales_items 对象)的 vector

那么具体该如何定义呢?

构造函数声明接口说明
vector<T> v1;vector保存类型T的对象,默认构造函数v1为空
vector<T> v2(v1);用v1拷贝构造v2
vector<T> v3(n,i);用n个 值为 i 的元素来构造v3
vector<T> v4(n);v4 用n个T类型的默认值来构造v4
vector<T> v5(InputIterator first, InputIterator last);使用迭代器来构造v5

具体的定义来看一下代码:

(1) vector<T> v1;构造一个空的存储T类型数据的 v1 对象
void Test1()
{
	vector<int> v1;
	vector<string> v2;
	vector<double> v3;
	vector<char> v4;
}

可以在这个容器中我们可以存储各种基本类型的数据,当然也可以存储自定义的数据类型

class A
{

};
void Test1()
{
	vector<A> v5;
}
(2) vector<T> v2(v1);拷贝构造
void Test2()
{
	vector<int> v1;
	v1.push_back(10);//尾插一个数据
	vector<int> v2(v1);
	cout << v2[0] << endl;//既然vector是一个相当于数组一样
	//的容器,那么肯定也是可以使用下标进行访问的
}

在这里插入图片描述

注意 使用另一个vector拷贝构造另一个vector对象时,两者的类型必须相同否则无法完成拷贝构造

(3) vector <T> v1(n,i);使用n个数据类型为T的 值为 i 的元素来构造 v1
vector<int> v1(10, 1);
vector<string> v2(10,"hi");
vector<double> v3(10,2.34);
vector<char> v4(10,'c');

在这里插入图片描述

(4) vector<T> v1(n);用n个T类型的默认值来构造v4

相比于 vector <T> v(n,i);这里没有给值,这时候就会只用该数据类型的默认值来构造 ,比如 int 类型的默认值 是 0 string 类型的是 一个空字符串相当于 --""
在这里插入图片描述

(5)vector<T> v1 (InputIterator first, InputIterator last); 使用迭代器来构造一个vector对象

之前我们已经学习过string 对迭代器有了一定的了解 ,vector同样也是可以使用迭代器,对vector对象进行一定的访问操作
先大概了解一下vector的迭代器构造方法:

void Test5()
{
	vector<int> v1(10, 5);
	vector<int> v2(v1.begin(), v1.end());
	for (auto e : v2)
	{
		cout << e << " ";
	}
}

在这里插入图片描述
这种迭代器构造有点像我们的拷贝构造,但又不仅仅这些,在讲到vector的迭代器方面会详细介绍;


四、vector 的增删改查 与 迭代器iterator

(1) vector 的插入删除
尾插 void push_back();
尾删 void pop_back();

了解了上面的vector的定义之后,大家对于vector的插入和其所介绍的动态增长还有访问元素的高效,在末尾添加和删除元素相对高效。还没有体会到,接下来大家可以体会一下。

	vector<int> v;
	v.push_back(1);
	v.push_back(3);
	v.push_back(2);
	v.pop_back();
	for (auto e : v)
	{
		cout << e << " ";//auto关键字的使用 范围for 
		//底层还是由迭代器实现
	}

在这里插入图片描述

在指定位置插入,删除

注意:这里的insert和earse 并非简单的给一个数组下标的位置,或者要删除的元素的值,这里是需要通过迭代器来进行相关的操作的,具体的需要了解了迭代器的相关知识才可以,这里做一个简单的示例:

	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	vector<int> ::iterator ite = v.begin()+1;
	//也就是删除第二个位置的数据
	v.erase(ite);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.insert(v.begin(),5);
	//相当于头插一个数据
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	v.insert(v.begin() + 2, 8);
	//在第指定一个位置插入一个数据
	for (auto e : v)
	{
		cout << e << " ";
	}

在这里插入图片描述
但是在一些情况下会出现迭代器失效的情况,会在以后的博客中详细介绍。

(2)vector 迭代器 iterator
iterator的使用接口说明
begin()获取第一个数据位置的 iterator
end()获取最后一个数据的下一个位置的iterator
rbegin取最后一个数据的reverse_iterator
rend()获取第一个数据的前一个位置的reverse_iterator
cbegin()获取 第一个数据位置的const_iterator
cend()获取最后一个数据的下一个位置的const_iterator

我们知道 除了使用下标来访问vector 对象的元素外,标准库还提供了另一种访问元素的方法,使用迭代器(iterator)迭代器是一种检查容器内元素并遍历元素的数据类型。

标准库为每一种标准容器(包括 vector)定义了一种迭代器类型。迭代器 类型提供了比下标操作更通用化的方法:所有的标准库容器都定义了相应的迭代器类型,而只有少数的容器支持下标操作。因为迭代器对所有的容器都适用,现 代 C++ 程序更倾向于使用迭代器而不是下标操作访问容器元素,即使对支持下 标操作的 vector 类型也是这样

在这里插入图片描述
关于迭代器类型 ,迭代器失效等会单独整理一篇博客,这里我们主要知道如何利用 vector 的begin end rbegin rend cbegin cend进行vector 的遍历以及一些相关的操作。

begin 和 end 操作

每种容器都定义了一对命名为 begin 和 end 的函数,用于返回迭代器。
在这里插入图片描述
这是 begin 的返回值 我们可以看到 它返回的是

序列容器开头的迭代器。
如果vector对象是Const限定的,则该函数将返回const迭代器。否则,它将返回迭代器.
成员类型迭代器和const迭代器是随机存取迭代器类型(分别指向元素和Const元素)

而 end 是不是就返回容器 vector 的末尾的迭代器呢呢?
事实上end 返回的是 最后一个元素的下一个位置的迭代器。
在这里插入图片描述
那么具体如何进行容器元素的访问和操作呢?

	vector<int>::iterator ite;//迭代器的定义 包括迭代器的类型 定义的变量
	ite = v.begin();
	while (ite != v.end())//不等于就保持其不会越界
	{
		cout << *ite << " ";//解引用来访问该位置的值
		++ite;
	}

在这里插入图片描述

rbegin 和 rend 操作

rbegin 和 rend 返回的迭代器恰恰相反
在这里插入图片描述
相当于反向的 开头和末尾 只不过 rbegin 返回 的是容器的末尾的迭代器 而 rend 返回的是容器的开头的前一个位置的迭代器,而反向迭代器 在标准库中也有专门的定义

void Test9()
{
	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	vector<int>::reverse_iterator rite;//反向迭代器的定义 包括迭代器的类型 定义的变量
	rite = v.rbegin();
	while (rite != v.rend())//不等于就保持其不会越界
	{
		cout << *rite << " ";//解引用来访问该位置的值
		++rite;
	}
}
cbegin 和 cend操作

既然有了正向迭代器也有了反向迭代器 那么这个 const 迭代器是用来干什么的呢?

其实 使用迭代器不仅可以对范围内的数据进行访问也可以进行修改,而const迭代器的出现就是为了不让其对数据进行修改只进行访问,可以通过代码进行比较;

	vector<int>::iterator ite = v.begin();
	while (ite != v.end())
	{
		*ite += 1;
		cout << *ite << " ";
		++ite;
	}
	cout << endl;
	vector<int>::const_iterator cite = v.cbegin();
	while (cite != v.cend())
	{
		//*cite += 1;//报错表示左值不可修改
		cout << *cite << " ";
		++cite;
	}

在这里插入图片描述
这就是使用迭代器进行访问查询 ,还有我们之前使用的下标访问,对下标进行遍历访问,
或者使用 auto 关键字 进行范围 for遍历
说到 auto 关键字 它还可以推断出迭代器也可以进行遍历 比如

	vector<int> v;
	v.push_back(1);
	v.push_back(2);
	v.push_back(3);
	v.push_back(4);
	for (auto e : v)
	{
		cout << e << " ";
	}
	cout << endl;
	auto ite = v.begin();//自动识别其类型
	while (ite != v.end())
	{
		cout << *ite << " ";
		++ite;
	}

五、vector的大小,容量等

容量空间接口说明
size()获取数据个数
capacity()获取容量大小
empty()判断是否为空
void resize (size_type n, value_type val = value_type());改变vector的size
void reserve (size_type n);改变vector放入capacity

注意:

size();
返回一个vector 对象的大小 ,但是注意在使用下标访问的时候注意数组的下标是从 0 开始的 所以在使用下标遍历的时候需要size()-1;

capacity();
因为vector是动态增长的,因此其容量不够时就会增容,在vs里一般是 1.5倍的增长。

和string 一样 ,为了提高效率一般可以提前设置其容量减少开空间时花费的代价,提高效率。

这里讲一个关于vector扩容的问题:
在STL源码剖析中关于当vector进行扩容的时候是以二倍进行扩容的,也就是说当要新增元素,而这时capacity==size的时候就会进行扩容 capacity变为原来的两倍:
在这里插入图片描述
当然也有的同学会发现在VS下是1.5倍扩容的:
在这里插入图片描述
而在Linux下使用g++编译器下时为:
在这里插入图片描述
可以看到是呈2倍进行扩容的,因此这只是编译器的原因导致的,只需要记住STL底层实现是 2倍扩容的即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值