STL学习之——vector向量容器使用详解

一、概述

vector是C++标准模板库中的一个重要组成部分,vector是一个向量容器,是一种动态数组。它能够存放各种类型的对象,简单地说,vector是一个能够存放任意类型的动态数组,可以动态改变大小。支持快速随机访问。
注意: vector将元素保存在连续的内存空间中,由于元素是连续存储的,由元素的下标来计算其地址是非常快速的,但是在vector容器的中间位置添加或删除元素会非常耗时,在一次插入或删除操作后,需要移动插入/删除位置之后的所有元素,来保持连续存储。而且,添加一个元素有时可能还需要分配额外的存储空间。在这种情况下,每个元素都必须移动到新的存储空间中。

使用前提:
头文件包含:#include <vector>
命名空间:vector属于std命名空间,因此需要通过命名限定,可在文件开头加上
using std::vector;
或者
using namespace std;
或者
在定义每个vector对象时,写成std::vector

二、vector对象创建

参考:https://blog.csdn.net/yjunyu/article/details/77728410?locationNum=10&fps=1
vector是一个模板类,其内部定义了构造函数和其他各种功能成员函数。其在定义对象时需注意初始化的一些细节,具体可以通过以下三个例子看出。
默认构造:
vector<int> vecExample1;//默认定义了一个长度为0,没有任何值的向量vecExample1,此时:vecExample1.size() = 0,vecExample1[0]会报错。如果用Debug来看的话,vecExample1为0,可通过push_back()函数来动态改变vecExample1的长度。
带单个整形参数的构造函数:描述向量的初始大小
vector<int> vecExample2(1);//调用vector的构造函数,并传递参数1,即定义了一个长度为1,并默认初始化为0的向量vecExample2,此时:vecExample2.size()=1,vecExample2[0]=0。如果用Debug模式来看的话,vecExample2为1。仍然可以通过push_back()函数来动态改变vecExample的长度。
vector<int> vecExample3(100);//与定义vector vecExample2(1)类似,只不过此时向量长度为100,并且每个向量元素都默认初始化为0。如果用Debug模式来看的话,vecExample3为100
vector<int> vecExample4(100,20); //与定义vector vecExample3(100)类似,只不过此时相当于调用了vector模板类的另一种类型的构造函数,并初始化向量的每个元素为20。
另外还可以利用数组对vector进行初始化:产生初始值为一个区间的向量(左闭右开区间)

int myints[] = { 32,71,12,45,26,80,53,33 };
std::vector<int> myvector(myints, myints + 8);// 32 71 12 45 26 80 53 33

创建二维vector矩阵:

  int m = 10,n = 5;
  vector<vector<int> > ivec(m ,vector<int>(n,10));
  cout<<ivec.size()<<" "<<ivec[0].size()<<endl;
  for(int row = 0; row<ivec.size();++row){
    for(int col = 0; col < ivec[0].size();++col){
      cout<<ivec[row][col]<<" ";
    }
    cout<<endl;
  }

vector构造程序示例:

#include <iostream>
#include <vector>
using namespace std;

typedef vector<int> INTVECTOR;// 自定义类型INTVECTOR
// 测试vector 容器的功能

int main()
{
    //vec1 对象初始为空
    INTVECTOR vec1;  

    //vec2 对象最初有10 个值为6 的元素  
    INTVECTOR vec2(10,6); 

    //vec3 对象最初有3 个值为6 的元素,拷贝构造
    INTVECTOR vec3(vec2.begin(),vec2.begin()+3); 

    // 声明一个名为i 的双向迭代器
    INTVECTOR::iterator iter;

    // 从前向后显示vec1 中的数据
    cout<<"vec1.begin()--vec1.end():"<<endl;

    for (iter =vec1.begin(); iter !=vec1.end(); ++iter)
        cout << *iter << " ";

    cout << endl;

    // 从前向后显示vec2 中的数据
    cout<<"vec2.begin()--vec2.end():"<<endl;

    for (iter =vec2.begin(); iter !=vec2.end(); ++iter)
        cout << *iter << " ";

    cout << endl;

    // 从前向后显示vec3 中的数据
    cout<<"vec3.begin()--vec3.end():"<<endl;

    for (iter =vec3.begin(); iter !=vec3.end(); ++iter)
        cout << *iter << " ";

    cout << endl;

    // 测试添加和插入成员函数,vector 不支持从前插入

    vec1.push_back(2);// 从后面添加一个成员
    vec1.push_back(4);
    vec1.insert(vec1.begin()+1,5);// 在vec1 第一个的位置上插入成员5

    // 从vec1 第一的位置开始插入vec3 的所有成员
    vec1.insert(vec1.begin()+1,vec3.begin(),vec3.end());

    cout<<"after push() and insert() now the vec1 is:" <<endl;

    for (iter =vec1.begin(); iter !=vec1.end(); ++iter)
        cout << *iter << " ";

    cout << endl;

    // 测试赋值成员函数
    vec2.assign(8,1);   // 重新给vec2 赋值,8 个成员的初始值都为1

    cout<<"vec2.assign(8,1):" <<endl;

    for (iter =vec2.begin(); iter !=vec2.end(); ++iter)
        cout << *i << " ";

    cout << endl;

    // 测试引用类函数
    cout<<"vec1.front()="<<vec1.front()<<endl;// vec1 第零个成员

    cout<<"vec1.back()="<<vec1.back()<<endl;// vec1 的最后一个成员

    cout<<"vec1.at(4)="<<vec1.at(4)<<endl;// vec1 的第五个成员

    cout<<"vec1[4]="<<vec1[4]<<endl;

    // 测试移出和删除
    vec1.pop_back();// 将最后一个成员移出vec1
    vec1.erase(vec1.begin()+1,vec1.end()-2);// 删除成员
    cout<<"vec1.pop_back() and vec1.erase():" <<endl;

    for (iter =vec1.begin(); iter !=vec1.end(); ++iter)
        cout << *i << " ";

    cout << endl;

    // 显示序列的状态信息

    cout<<"vec1.size(): "<<vec1.size()<<endl;// 打印成员个数

    cout<<"vec1.empty(): "<<vec1.empty()<<endl;// 判断容器是否为空

    return 0;
}  

三、vector对象操作

遍历vector中元素

(1) 迭代器方法:

vector<string>::iterator it;
for(it=myVec.begin();it!=myVec.end();it++)
{
cout<<*it<<endl;
}

vector中元素访问方法:

vector c;
c.at(index);
c[index];
c.front();
c.back();
其中,c.at(index)函数返回值是引用,既可以取出元素值,也可以直接对向量中的指定位置元素赋值,at函数能够提供越界检查,如果出现访问越界,会给出out of range错误;c[index]——类似数组的访问方式,同样返回值是引用类型,必须确保下标有效,不保证访问有效;c.front()返回第一个元素;c.back()返回最后一个元素。
为了保证访问元素的安全性与便利性,建议使用迭代器或at()函数访问容器中的数据元素,优先选用迭代器。

元素查找和搜索:

查找vector型容器中的元素,可以使用STL的通用算法find()。有关条件搜索相关元素可以使用算法find_if()。这两个算法函数均可以使用迭代器,两个迭代器参数决定了搜索和查找的范围。函数返回值均为迭代器类型。
find函数应用:
http://blog.chinaunix.net/uid-22431962-id-125367.htmlhttp://blog.csdn.net/pengrui18/article/details/21446929
http://blog.csdn.net/bobodem/article/details/49386131
http://blog.csdn.net/huangyimin/article/details/6133650
http://www.cnblogs.com/fnlingnzb-learner/p/5889026.html

复制操作:
变量之间互不影响

删除vector中指定元素:

要想从容器中删除其中的某个元素,必然需要调用对应容器的成员函数,即没有一个泛化模板函数来实现删除功能。
vector中可以使用erase函数来移除vector中的指定元素。
一般会用remove配合erase来实现元素移除,有关remove函数的用法和注意事项如下:
https://blog.csdn.net/vbanglev/article/details/1512521
https://www.cnblogs.com/xudong-bupt/p/3522457.html
C++ vector中实际删除元素使用的是容器vecrot中std::vector::erase()方法。
C++ 中std::remove()并不删除元素,因为容器的size()没有变化,只是元素的替换。

利用erase实现元素删除
https://blog.csdn.net/yockie/article/details/7859330
https://blog.csdn.net/xjm199/article/details/24841071
https://www.cnblogs.com/cLockey/p/3770622.html

利用remove_if配合erase删除vector中所有满足条件的元素:
https://www.haroldserrano.com/blog/c-tip-17-how-to-remove-vector-elements-in-a-loop

常用功能函数:

可以使用的功能:

std::vector<int> c;
c.back()        返回向量c中最后一个元素
c.begin()           返回指向容器第一个元素的迭代器
c.clear()         移除容器中所有数据,直接将vector变为空向量,即c.empty()为true。
c.capacity()   返回容器中实际能够容纳的元素数量
c.empty()         判断容器是否为空
c.end()             返回指向容器最后一个元素的迭代器
c.erase(pos)        删除pos位置的数据,向量长度会减少。pos需是迭代器类型
c.erase(beg,end) 删除[beg,end)区间的数据
c.front()         传回第一个数据,即对向量中第一个元素的引用
c.insert(pos,elem)  在pos位置插入一个elem拷贝
c.pop_back()     删除最后一个数据。
c.push_back(elem) 在尾部加入一个数据。
c.resize(num)     重新设置该容器的大小
c.reserve(num) 预先设置容器的大小
c.size()         回容器中实际数据的个数。

函数解释:
注意reserve()函数和resize()函数的不同:reserve()函数可以预先设置容器的大小,resize()函数可以修改容器的大小。
例如:

vector<int> vecScore;
vecScore.reserve(10);

此时如果输出vecScore.size(),其值仍然为0,但是vecScore.capacity()为10;

vector<int> vecScore;
vecScore.resize(10);

此时vecScore.capacity()和vecScore.size()都为10,并且默认初始化每个元素为0;
可以这么理解:vector是一种动态数组,为了实现动态,编译器在每次修改其长度的时候都会做一系列处理,而如果我们事先知道所需vector最多需要容纳的数量,就可以用reserve函数先保留一块内存,后面再添加元素的时候能够大幅减少处理,而resize函数则代表了真正的我们用到的容器大小,即我们实际用的容器大小都是用size()来指示。
注意:用resize() 函数改变容器大小时可以传递初始值,即vec.resize(10,init),用init来初始化容器元素,此时需要特别注意,该初始值只保证在将vector中元素数量由小变大时给新添加的元素赋初值,并不会影响vector中本来就有的元素!!!
reserve 函数分配出来的内存空间,只是表示vector可以利用这部分内存,但vector不能有效地访问这些内存空间,访问的时候就会出现越界现象,导致程序崩溃。

size()和capacity()都用来统计vector容器的元素。其中size()函数返回容器中现有的元素数量,capacity()返回容器中实际能够容纳的元素数量。

E.g.
vector<int> vecInt;
 vecInt.reserve(5);
  cout<<vecInt.capacity()<<endl;
  cout<<vecInt.size()<<endl;
输出为:
5
0

注:虽然vector可以动态申请内存,但是如果事先知道需要存储的最大容量,可以用reserve函数申请足够的内存,为vector容器预留相应的元素位置,减少在元素添加过程中内存的动态申请过程。

void resize (size_type n);
void resize (size_type n, value_type val);

resize函数重新分配大小,改变容器的大小,并且创建对象
当n小于当前size()值时候,vector首先会减少size()值 保存前n个元素,然后将超出n的元素删除(remove and destroy)
当n大于当前size()值时候,vector会插入相应数量的元素 使得size()值达到n,并对这些元素进行初始化,如果调用上面的第二个resize函数,指定val,vector会用val来初始化这些新插入的元素
当n大于capacity()值的时候,会自动分配重新分配内存存储空间。

push_back函数其实是操作拷贝构造函数加入元素:
https://www.cnblogs.com/chuanyang/p/6047470.html

assign函数
函数原型为:

1:void assign(const_iterator first,const_iterator last); 
2:void assign(size_type n,const T& x = T());
第一个相当于个拷贝函数,把first到last的值赋值给调用者;(注意区间的闭合) 
第二个把n个x赋值给调用者;

四、其他

(1) 对vector中元素排序

待写

(2) Vector清空数据与释放内存相关

https://blog.csdn.net/a272846945/article/details/51182144
https://blog.csdn.net/u012580994/article/details/43932701
主要是弄清楚clear()和swap()函数以及size()和capacity()的区别和联系;
使用clear()只是腾空了容器中数据,但其实并没有释放内存,也即size()为0了,但是capacity仍然不为0。
有关vector内存相关注意事项:
1、内存空间会自增长,需要存储空间时候,加倍当前容量的分配策略实现重新分配。例如,当前capacity为50,当添加第51个元素时,预留空间不够用了,vector容器会重新分配大小为100的内存空间,作为新连续存储的位置;
2、内存占用空间只增不减,clear(),erase()都是清空元素,但是空间还是没有释放。
3、需要动态清理占用空间可以使用deque(),线性访问,还支持从前添加元素。
4、默认情况下vector执行的是一种“用时间换空间”的做法,耗费大量的时间去不停的释放旧空间,开辟新空间。所以使用vector,最好还是预估一下数据大小,尽可能的减少释放和析构的次数。
5、vector的析构函数由系统调用,比如局部变量使用完。
6、手动释放内存:

vector nums;  
nums.push_back(1);
vector().swap(nums);||或者nums.swap(vector ())

7、vector存的指针的时候,释放vector之前,需要把指针也释放

 for (vector<void *>::iterator it = v.begin();  it != v.end(); it ++) 
    if (NULL != *it) 
    {       delete *it; 
        *it = NULL;
    }
    v.clear();

详细解释:https://blog.csdn.net/cws1214/article/details/47984053

(3) 对vector中数据求均值和方差
https://blog.csdn.net/caroline_wendy/article/details/24623187

五、补充知识

作为数组的最佳替代者,vector容器有无数的优势,但是在需要保存一组固定个数的数据时,例如一年的12个月,一周的七天,此时用vector并不是最佳选择,STL提供了替代数组的第二种容器选择——array容器。

猜你喜欢

转载自blog.csdn.net/zhanghm1995/article/details/81509866