STL基础5:vector容器的使用总结

时间:2022-10-23 04:16:19

一.vector使用构造函数的四种初始化方式

//1.默认构造函数,长度为0的空向量
 //vector<int> v1;
 //2.带有单个整形参数的构造函数,长度为50的空向量
 //vector<int> v2(50);
 //vector<int> v3(50,1);//长度为50,初始值为1的向量
 //3.复制构造函数,构造一个新的向量v4,作为已存在的向量v2的完全复制
 //vector<int> v4(v2);
 //4.带两个常量参数的构造函数,产生初始值为一个区间的向量。区间由一个左闭右开的半开区间[first,last) 来指定
 //vector<int> v5(first_Iter,end_Iter);

 

假设有int数组: int  v1[10] = {0,1,0,0,3,0,0,4,4,4};
第一种初始化方式:  带有单个整形参数的构造函数

vector<int> vecInit1(10);//和vector<int> vecInit1;vecInit1.resize(10);一样
 for(int i=0;i<10;i++)
 {
  vecInit1.push_back(v1[i]);//将数组v1的元素添加到向量init1的最后
 }

 //注意,push_back每执行一次会调用一次Vector的对象vecInit1的构造函数和析构函数,效率很低啊慎用这种容器动态添加元素

第二种初始化方式: 带两个迭代器参数的构造函数
 //左闭右开区间,左边从数组[0]的地址开始拷贝数组元素,到右边的数组[10]之前一个的地址结束
 //也就是说如果要拷贝数组v1里的10个元素,就必须在右边,数组最后一个元素的地址再加上1(&v1[9]+1)

vector<int> vecInit2(&v1[0],&v1[9]+1);

第三种初始化方式:  默认构造函数

vector<int> vecInit3;
vecInit3.reserve(10);//预先设置vector的元素容纳数量,提前预留空间
vecInit3.insert(vecInit3.begin(),&v1[0],&v1[9]+1);//后面两个参数同样也是左闭右开

 第四种初始化方式:  复制构造函数

 vector<int> vecInt4(vecInit3);

 

 

二.vector的三种遍历向量里元素的方式

假设有一个数组:

int  v2[10] = {0,1,0,0,3,0,0,4,4,4};
 vector<int> vecForeach1(&v2[0],&v2[9]+1);


 第一种:.遍历向量vecForeach1的下标方式

 for(int i=0;i<vecForeach1.size();i++)
 {
  printf("遍历向量vecForeach1的下标方式取vecForeach1里的元素为%d\n", vecForeach1[i]);
 }



  第二种.使用迭代器遍历vecForeach1向量方式

for(vector<int>::iterator iter=vecForeach1.begin();iter<vecForeach1.end();iter++)
 {
  printf("使用迭代器遍历vecForeach1向量方式取vecForeach1里的元素为%d\n", *iter);
 }

//可以优化上面的代码

1.使用typedef vector<int>::iterator Vector_Iter将vector<int>::iterator另外定义一个名字为Vector_Iter
 //2.先计算vecForeach1.end()的值,不用在for里每次都去计算一次;
 //3.在for里使用iter的前置++,效率更高
 //4.写算法的时候尽量使用!=比较迭代器,因为<对于很多非随机迭代器没有这个操作符
 //优化后代码为:

 typedef vector<int>::iterator Vector_Iter;
 Vector_Iter iterEnd=vecForeach1.end();
 for(Vector_Iter iter=vecForeach1.begin();iter!=iterEnd;++iter)
 {
  printf("优化后的使用迭代器遍历vecForeach1向量方式取vecForeach1里的元素为%d\n", *iter);
 }


 第三种:.使用STL的算法类里的for_each,需要包含头文件#include <algorithm>,

首先需要定义一个Vector_ForeachCoutFun的方法将元素输出

void Vector_ForeachCoutFun(int &outIndex)
{
	printf("使用foreach输出vecForeach1里的元素为%d\n",outIndex);
}

然后调用for_each将元素输出

for_each(vecForeach1.begin(),vecForeach1.end(),Vector_ForeachCoutFun);



  三.vector的删除操作

      1. 可以使用四种方式删除vector中的元素:

第一种是使用向量容器vector的成员函数erase(使用这种方式并不能清除删除元素所占的内存空间),由于向量容器vector中的元素在内存中都是按顺序排放的,所以删除某个元素后,会导致迭代器失效,所以或者(重新更新迭代器begin和end)或者(更新迭代器end和利用erase返回指向被删除元素的下一个元素的有效迭代器更新)这两种方式都可以使迭代器不失效;

 SAMPLES:

int  v3[10] = {0,1,2,3,4,5,6,7,8,9}; 
vector<int> vecDeleted1(&v3[0],&v3[9]+1);

typedef vector<int>::iterator Vector_Iter;
Vector_Iter iterEnd=vecDeleted1.end();
for(Vector_Iter iter=vecDeleted1.begin();iter!=iterEnd;++iter)
{
      vecDeleted1.erase(iter);//使用erase迭代器失效
		
}

上面的代码是要依次删除vecDeleted1里的所有元素,但是会有一个问题,当删除第一个元素0后,再次遍历的时候,迭代器会报错,这就是迭代器失效造成的,所以可以使用

(更新迭代器end和利用erase返回指向被删除元素的下一个元素的有效迭代器更新)的方式更新迭代器,修改代码如下:

int  v3[10] = {0,1,2,3,4,5,6,7,8,9}; 
vector<int> vecDeleted1(&v3[0],&v3[9]+1);

typedef vector<int>::iterator Vector_Iter;
//Vector_Iter iterEnd=vecDeleted1.end();
for(Vector_Iter iter=vecDeleted1.begin();iter!=vecDeleted1.end();++iter)
{
   iter=vecDeleted1.erase(iter);//使用erase返回指向被删除元素的下一个元素的有效迭代器更新
}

将迭代器iter更新到删除元素的下一个元素,然后在for循环里使用vecDeleted1.end()代替原先的iterEnd,每次遍历的时候都更新一下vector的最末元素迭代器end()(要是不跟新这个,那么使用原先的end()指向了一个未知区域,程序当然报错)这样程序就不会报错

使用erase删除特定元素3的方式:

Vector_Iter iterVecDel=find(vecDeleted1.begin(),vecDeleted1.end(),3);
if(iterVecDel!=vecDeleted1.end())
{
   vecDeleted1.erase(iterVecDel);	
}

首先使用STL算法类里的find方法,找到元素3,返回一个指向元素3的迭代器iterVecDel1,然后使用erase方法将元素3删除

思考:

如果使用erase删除了一个向量vector元素,那么其他元素在内存中是怎么移动的呢

vecDeleted1:0,1,2,3,4,5,6,7,8,9      vecDeleted1[9]==9

删除元素3后:

Vector_Iter iterVecDel=find(vecDeleted1.begin(),vecDeleted1.end(),3);
if(iterVecDel!=vecDeleted1.end())
{
 vecDeleted1.erase(iterVecDel);
 
}

vecDeleted1:0,1,2,4,5,6,7,8,9

那么原先vecDeleted1[9]的位置元素是什么呢,那块内存还有元素吗,答案是肯定的,由于vector使用erase删除元素时,虽然将删除的元素从向量中移走了,然后移走后面的所有元素都是很有次序的往前移了一步,那么最后一块内存里的值原先是9,9向前移了一步,那么原先的那块内存vecDeleted1[9]还是9,并没有删除那块9这个元素,所以使用erase虽然从表面上看向量vector里的元素变少了,但是实际暂用的内存可没有变少哦!切记,既然erase不能把该死的元素从内存中移除,那不是很危险,该怎么办呢,所以下面的第二种方法就能彻底的解决这个问题。

 

第二种是使用STL算法类里的remove函数和erase搭配的方式进行删除(此方式不用更新迭代器)(实际上在vector容器使用remove函数只是将要删除的元素放到了容器的最后,不会删除元素。在STL所有容器中,唯一一个使用remove函数做了删除操作的是list容器的remove)    

SAMPLES:

int  v3[10] = {0,1,2,3,4,5,6,7,8,9}; 
vector<int> vecDeleted1(&v3[0],&v3[9]+1);

typedef vector<int>::iterator Vector_Iter;
for(Vector_Iter iter=vecDeleted1.begin();iter!=vecDeleted1.end();++iter)
{
    vecDeleted1.erase(remove(vecDeleted1.begin(),vecDeleted1.end(),*iter),vecDeleted1.end());
}

remove在一个给定范围内删除满足一定条件的元素,删除完成后,向量的大小不变,也就是说,它仅仅是将要删除的元素放到容器最后,而将所有未删除的元素前移而已。而且它返回的是指向那个移动元素位置之后的那个迭代器,所以上面的代码在使用remove之后,返回的迭代器指向向量的最后一个元素,也就是:- - vecDeleted1.end()。

然后在调用erase将向量的最后一个元素移除,由于最后一个元素后面就没有向量数据了,所以也不会往前移动任何数据,所以这样就能将向量最后一个元素移除而且还能清空这块内存。使用这种方式就没必要更新迭代器了,因为移除向量的末尾元素不会对前面的元素迭代器造成影响。只需再每次for循环更新一下end迭代器就行。

删除特定元素的方式(删除元素3):

vecDeleted1.erase(remove(vecDeleted1.begin(),vecDeleted1.end(),3),vecDeleted1.end());

 

第三种是使用vector的成员函数clear()(清空向量里的所有元素)

第四种是使用vector的成员函数pop_back()(移除向量里的最后一个元素)


vector删除元素总结:

1.使用erase方法不能清元素所占内存,只能将向量里的元素“表面的”移走;使用erase方法和remove方法配合使用可以真正清除向量里的元素和其所占内存,所以要删除vector里的元素就必须使用erase和remove配套使用

2.删除元素之后应该将相应的(begin迭代器,end迭代器)或(erase返回的迭代器,end迭代器)更新一下
 

四.vector的插入操作

第一种插入:vector的插入操作是使用vector的成员函数insert()在向量的任意位置插入一个元素,插入元素操作和删除元素的操作完成后都是需要更新失效的迭代器,有一点不同的是插入操作是一定会重新分配内存的,而删除操作是不会自动重新分配内存的,所以插入操作的迭代器是一定要更新的,和先前的迭代器不一样。

SAMPLES:

int  v3[10] = {0,1,2,3,4,5,6,7,8,9}; 
vector<int> vecDeleted1(&v3[0],&v3[9]+1);

for(Vector_Iter iter=vecDeleted1.begin();iter!=vecDeleted1.end();++iter)
{
   iter=vecDeleted1.insert(vecDeleted1.end(),10);
}

 

上面的iter=vecDeleted1.insert(vecDeleted1.end(),10);

insert函数返回的是指向插入新元素的迭代器iter,也就是说iter指向新插入的元素10

 

第二种插入:使用vector的成员函数push_back()(在向量最后添加一个元素,容器的容量也会随着扩展)

 

五.向量类vector的所有成员函数

1.capacity() ---vector所能容纳的元素数量(向量类vector特有的成员函数

2.size()---vector里的实际元素个数(容器大小)

3.reserve(num)---设置vector最小所能容纳的元素数量(capacity增大num)(向量类vector特有的成员函数

     3.1  只是在堆区使用new操作符预备申请了连续的内存空间,内存里的指针未知,也未初始化该连续内存,所以不能赋值使用下标操作[0]=来添加元素,只能使用push_back()和insert()向新申请的空间添加元素

     3.2 使用该方法预留空间,后面添加元素就不会不断重新分配内存

4.resize(num)---改变vector容器的大小(size增大num,capacity线性增大)

     4.1  申请了内存空间和空间中的指针,可以使用下标操作[0]=来添加元素(不会分配内存),也能使用push_back()(会重新分配内存)和insert()(在向量vector的末尾插入新元素会重新分配内存)

     4.2  使用该方法不断的改变容器大小会重新分配内存,效率很低

注意:如果向量vector原本里头就有元素,那么使用reserve和resize都不会对向量里原本的元素造成影响,也就是原来向量的元素还是存在

SAMPLES:

vector<int> vecMembers1;
vecMembers1.reserve(10);
vecMembers1.size();//0
vecMembers1.capacity();//10
vecMembers1.insert(vecMembers1.begin(),0);//在第0个元素之后插入元素0
vecMembers1.insert(vecMembers1.end(),1);//在第1个元素之后插入元素1
vecMembers1.push_back(2);//在第2个元素之后插入元素2
vecMembers1.size();//3
vecMembers1.capacity();//10

vecMembers1.resize(100);
vecMembers1.size();//100
vecMembers1.capacity();//100
vecMembers1[3]=3;
vecMembers1.insert(vecMembers1.end(),4);//在第100个元素之后插入元素4
vecMembers1.push_back(5);//在第101个元素之后插入元素5
vecMembers1.size();//102
vecMembers1.capacity();//150