从零开始学c++之STL——vector的介绍和使用

Posted 努力学习的少年

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了从零开始学c++之STL——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的使用

1.vector对象的创建

创建vector对象的模板:vector<类型名> 对象,其中的类型名可以是任意类型,如int,char,double,string等等。
如下:

 void vectortest()
{
	vector<int> v1(3, 3);//创建一个v1的对象,里面存储的是int类型的数据
	vector<char>v2(3, 'a');//创建一个v1的对象,里面存储的是char类型的数据
	vector<string>v3(3, "张三");//创建一个v1的对象,里面存储的是string类型(字符串)的数据
}

通过调试,可以看出v1存储3个int类型的数据,v2存储3个char类型的数据,v3存储3个string类型的数据。

构造函数声明接口说明
vector()无参的构造函数
vector(const vector v)拷贝构造
vector (InputIterator first, InputIterator last)使用迭代器进行初始化构造
vector(size_type n, const value_type& val = value_type())构造并初始化n个val
void vectortest1()
{
	vector<int> v1;//无参构造
	vector<int> v2(10, 3);//构造并初始化10个3
	vector<int>v3(v2);//拷贝构造
	vector<int>v4(v2.begin(), v2.end());//利用迭代器将v2中的数据构造出v4
}

2.迭代器 iterator的使用

项目说明
begin()+end()begin()返回的是vector对象的第一个数据地址,end()返回的是最后一个数据的下一位置的地址
rbegin()+rend()rbegin()获取的是vector对象的最后一个数据的地址,rend()获取的是vector对象第一个数据的前一个位置的地址。


3.vector空间

接口接口说明
empty判断是否为空
size获取数据个数
capacity获取容量的空间大小
resize改变size的大小
reserve改变容量的大小
	void vectortest()
{
	vector<int> v1(3, 3);
	cout << v1.size() << endl;//获取v1中的数据个数为3
	cout << v1.capacity() << endl;//获取v1中的容量为3
	v1.resize(10);//将v1的size从3变为10,同时capacity也会从10变为20
	v1.
	cout << v1.size() << endl;//获取v1中的数据个数为10
	cout << v1.capacity() << endl;//获取v1中的容量为10
	v1.reserve(20);//将capacity从10变为20,size不变
	cout << v1.size() << endl;//获取v1中的数据个数为10
	cout << v1.capacity() << endl;//获取v1中的容量为20
}

补充:
1.capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。c++标准只是定义vector和它的接口的功能,但每个编译器底下实现这些接口函数的可能会有所差异,所以一段相同的代码在vs编译器可能运行得起来,在linux下可能运行不起来,也可能某些数运行出来有所差异。
2.reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。

4.vector的增删查改

接口接口说明
push_back尾插数据
pop_back尾删数据
insert在position之前插入一个数据
erase删除position位置的数据
swap交换两个vector的数据空间
operator[]像数组一样去访问vector的数据
find查找,这个不是vector上的接口函数,而是在算法里面的.。


注明:任何容器都可以使用find,只要传迭代器给它,它就可以在这个迭代区间进行查找某个数据,找到了就返回地址,如果找不到,就返回end()。

输出的结果:

void vectortest4()
{
	vector<int> v1(3, 1);//创建v1,并初始化3个1
	vector<int> v2(4, 2);//创建v2,并初始化4个2
	v1.swap(v2);//将v1和v2中的数据进行交换,包括capacity和size
}

三.迭代器失效的问题

迭代器(iterator)是一个可以对其执行类似指针的操作(如:解除引用(operator*())和递增(operator++()))的对象,我们可以将它理解成为一个指针。
什么是迭代器失效呢?我们先来看一个例子:

void vectortest4()
{
	vector<int> v1;
	v1.push_back(1);
	v1.push_back(2);	
	v1.push_back(3);
	v1.push_back(4);//v1中尾插4个数据

	vector<int>::iterator pos = find(v1.begin(), v1.end(), 3);
	v1.insert(pos, 0);//在3的位置之前插入一个0,此时pos位置指向的数据就变为0,此时迭代器失效
}

1.第一种情况:我们通过迭代器找到3的位置pos,然后在pos位置插入0,插入0之后,pos指向的数据就由3变成0了,迭代器就失效了。我们再去用这个迭代器pos,有的编译器就会报错。


2.第二种情况:我们通过迭代器找到3的位置pos,然后将3给删掉,这时迭代器pos指向的数据就变为4,同样,迭代器也会失效。

3.第三种情况:我们通过迭代器找到3的位置pos,然后在通过pos在3之前插入一个0,但是v1容量已经最大,此时就需要增容,就先开辟一块更大的空间,把久空间的数据拷贝给新空间,再把久空间给处理掉,所以pos就变为野指针(在vector底下层,迭代器就是指针)。这时候迭代器也失效。不能再用它


总结:1.如果我们利用迭代器将在某个数据之前插入一个数据之后,或者利用迭代器删除某个数据之后,迭代器指向内容就会改变,也就是迭代器的意义改变,迭代器就失效,就有可能引发错误。
2.如果想插入某个数据,出现增容的情况,pos指向的空间已经释放,此时的pos就变成野指针。迭代器也失效
所以:迭代器失效的解决办法是:在使用之前,对迭代器重新赋值即可。

写一段代码,删除vector中的所以偶数
接下来我们在看一下下面这段代码是否正确:

int main()
{
 vector<int> v{ 1, 2, 3, 4 };
 auto it = v.begin();
 while (it != v.end())
 {
 if (*it % 2 == 0)
 v.erase(it);
 ++it;
 }
 
 return 0; }

在vs测试了一下,发现这段代码会奔溃。为什么呢?
我们在删除数据时,后面的数据就会往前挪动,end()也会向前挪动一步,然后it在++,如果删除最后一个位置的数据,此时it和end()就不会相遇,最终程序奔溃。

所以我们对代码改动一下,如果删除时,it就不会走一步,没有删除,it才走一步

int main()
{
 vector<int> v{ 1, 2, 3, 4 };
 auto it = v.begin();
 while (it != v.end())
 {
 if (*it % 2 == 0)
 it = v.erase(it);
 else
 ++it;
 }
 return 0; 
 }

以上是关于从零开始学c++之STL——vector的介绍和使用的主要内容,如果未能解决你的问题,请参考以下文章

C++从青铜到王者第九篇:STL之vector类的初识

从零开始写STL-容器-双端队列

从零开始写STL—容器—vector

C++ STL之vector详解

C++ STL之vector详解

acm的STL应用之vector篇