首页 > 编程笔记

C++ vector容器的使用(非常详细)

vector 容器与动态数组相同,在插入或删除元素时能够自动调整自身大小,即 vector 容器能够自动处理存储数据所需的空间。

vector 容器中的元素放置在连续的内存空间中,可以使用迭代器对其进行访问和遍历。vector 容器的存储结构如图1所示。

图1 vector容器的存储结构

图1 vector容器的存储结构

vector 容器在插入元素时,插入位置之后的元素都要被顺序地向后移动,因此,在总体上 vector 容器插入操作效率并不高。插入位置越靠前,执行插入所需的时间就越多,但在 vector 容器尾部插入元素的效率比较高。

删除 vector 容器中的元素时,被删除元素后面的元素会顺序向前移动,将被删除元素留下的空位补上。删除操作的效率和插入类似,被删除元素越靠前,删除操作所需的时间就越多。

下面讲解 vector 容器的常见用法。

创建vector容器

vector 模板中定义了多个重载构造函数,因此vector容器的创建与初始化也有多种方式。vector 容器的创建方式主要有四种。

1) 指定容器大小

指定容器大小的格式如下:

vector<元素类型> 对象名(容器大小);

在上述格式中,“<>”中的元素类型名表示容器中元素的类型,如 int 表示容器中存储的是 int 类型的数据。“()”中指定容器大小。示例代码如下所示:
vector<int> v1(10);
vector<string> v2(5);
上述代码说明:
需要注意的是,vector 对象在定义后所有元素都会被初始化,如果是基本数据类型的容器,则都会被初始化为 0;如果是其他类型容器,则由类的默认构造函数初始化。

2) 指定初始值

在创建 vector 容器时,可以同时指定 vector 容器大小和初始值,格式如下所示:

vector<元素类型>对象名(容器大小,元素初始值);

按照上述格式创建 vector 容器,示例代码如下所示:
vector<int> v1(10, 1);  
vector<string> v3(3, "aa");
上述代码说明:

3) 列表初始化

C++11 新标准还提供了另外一种 vector 容器初始化方式,即用值的列表初始化,示例代码如下所示:
vector<int> v1{1, 2};   //v1有两个元素
vector<string> v2 = { "a", "b", "c" }; //v2有三个元素
使用列表初始化时,带“=”符号与不带“=”符号均可。

4) 初始化状态为空

初始化状态为空,即创建 vector 对象时不指定大小也不指定初始值,示例代码如下所示:
vector<int> v1;   //创建存储int类型数据的容器v1
除了上述方式,vector 容器还可以用另外一个 vector 容器完成初始化,示例代码如下所示:
vector<int> v1(10, 1);
vector<int> v2(v1);   //用容器v1初始化容器v2 
vector<int> v3 = v2;   //用容器v2给容器v3赋值
需要注意的是,用一个 vector 容器初始化另一个容器或相互赋值时,两个 vector 容器的元素类型必须相同。

获取容器容量和实际元素个数

vector 容器的容量与容器实际元素个数并不一定相同,容器容量指容器最多可以存储的元素个数,是容器预分配的内存空间。而容器实际元素个数可能小于容器容量。

vector 提供了两个函数 capacity() 和 size(),分别用于获取容器容量和容器实际元素个数,这两个函数的调用形式如下所示:
v.capacity();
v.size();
上述代码说明:

访问容器中的元素

由于 vector 重载了[]运算符,因此可以使用索引方式访问 vector 容器中的元素。此外,vector 容器还提供了一个成员函数 at(),用于随机访问容器中的元素。

at() 函数调用形式如下所示:
v.at(int idx);
在上述 at() 函数的调用形式中,参数 idx 表示索引。at() 函数返回值为索引指向的数据。

赋值函数

vector 容器中的元素可以在创建容器时指定,也可以通过[]运算符完成赋值。除此之外,vector 容器还提供了一个成员函数 assign(),用于给空的 vector 容器赋值。

assign()  函数有两种重载形式,分别如下所示:
v.assign(n, elem);   //将n个elem元素赋值给容器
v.assign(begin, end);   //将[begin, end)区间中的元素赋值给容器
assign() 函数的两种重载形式都可以完成容器元素赋值:
注意,区间是左闭右开,即第一个区间值可以使用,最后一个区间值不可以使用。

获取头部和尾部元素

vector 容器提供了 front() 函数与 back() 函数,分别用于获取容器的头、尾元素。

front() 函数和 back() 函数调用形式如下所示:
v.front();   //获取容器头部元素(第一个元素)
v.back();   //获取容器尾部元素(最后一个元素)
v.front();   //获取容器头部元素(第一个元素)
v.back();   //获取容器尾部元素(最后一个元素)

从尾部插入和删除元素

vector 容器提供了一对函数 push_back() 与 pop_back(),分别用于从尾部添加元素和删除尾部元素。

push_back() 函数和 pop_back() 函数调用形式如下所示:
v.push_back(type elem& t);
v.pop_back();

【示例1】下面通过案例演示 vector 容器的用法,C++ 代码入下:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> v;   //创建一个空的vector容器v
    for(int i=0;i<10;i++)
        v.push_back(i+1);   //从尾部向容器v中插入10个元素
    for(int i=0;i<10;i++)
        cout<<v[i]<<"  ";   //输出容器v中的元素
    cout<<endl;
    v.pop_back();   //删除尾部元素
    for(int i=0;i<9;i++)   //此时元素个数为9
        cout<<v[i]<<"  ";   //输出容器v中的元素
    cout<<endl;
    return 0;
}
运行结果:

1  2  3  4  5  6  7  8  9  10
1  2  3  4  5  6  7  8  9

示例分析:

容器的迭代器

vector 容器提供了迭代器,通过迭代器可以访问、修改容器中的元素。

vector容器提供了 iterator、const_iterator、reverse_iterator 和 const_reverse_iterator 四种迭代器,这四种迭代器作用分别如下:

在使用迭代器之前,必须先定义迭代器对象,vector 容器迭代器对象的定义格式如下所示:

vector<元素类型> 迭代器 迭代器对象名称;

在实际编程中,通常将创建的迭代器对象简称为迭代器。迭代器可以执行++−−、与整数相加减等算术运算,以达到遍历容器元素的目的。

vector 提供了获取上述四种迭代器的成员函数,如表 1 所示。

表1  vector容器获取迭代器的函数含义
函数 含义
begin() 返回容器的起始位置的迭代器 iterator
end() 返回迭代器的结束位置的迭代器 iterator
rbegin() 返回容器结束位置作为起始位置的反向迭代器 reverse_iterator
rend() 返回反向迭代的最后一个元素之后的位置的反向迭代器 reverse_iterator
cbegin() 返回容器中起始位置的常量迭代器 const_iterator,不能修改迭代器指向的内容(C++11)
cend() 返回迭代器的结束位置的常量迭代器 const_iterator,不能修改迭代器指向的内容(C++11)
crbegin() 返回容器结束位置作为起始位置的迭代器 const_reverse_iterator(C++11)
crend() 返回第一个元素之前位置的常量迭代器 const_iterator (C++11)

注意,迭代器遍历容器到达尾部时,指向最后一个元素的后面,而不是指向最后一个元素,即使用 end() 函数、rend() 函数、cend() 函数和 crend() 函数获取的迭代器,指向最后一个元素后面的位置,而不是指向最后一个元素。

【示例2】下面通过案例演示迭代器的用法,C++ 代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<int> c={1,2,3};   //创建vector容器c
    vector<int>::iterator pos;    //定义iterator迭代器pos
    vector<int>::reverse_iterator pos_r;  //定义reverse_iterator迭代器pos_r
    cout<<"iterator迭代器:";
    for(pos=c.begin();pos!=c.end();++pos)   //使用迭代器pos遍历容器c中的元素
    {
        cout<<*pos<<" ";
    }
    cout<<endl<<"reverse_iterator迭代器:";
    //使用迭代器pos_r反向遍历容器c中的元素
    for(pos_r=c.rbegin();pos_r!=c.rend();++pos_r)
    {
        cout<<*pos_r<<" ";
    }
    return 0;
}
运行结果:

iterator迭代器:1 2 3
reverse_iterator迭代器:3 2 1

示例分析:

小提示:迭代器失效:vector 容器是一个顺序容器,在内存中是一块连续的内存,当插入或删除一个元素后,内存中的数据会移动,从而保证数据的连续。当插入或删除数据后,其他数据的地址可能会发生变化,迭代器获取容器位置信息的数据不正确,即迭代器失效,会导致访问出错。

在任意位置插入和删除元素

vector 提供了一对向容器任意位置插入和删除元素的函数 insert() 与 erase()。

其中,insert() 函数用于向容器中插入元素,它有三种重载形式,分别如下所示:
v.insert(pos, elem);   //在pos位置插入元素elem 
v.insert(pos, n, elem):   //在pos位置插入n个elem元素
v.insert(pos, begin, end);   //在pos位置插入[begin, end)区间的数据
erase() 函数用于删除容器中的元素,它有两种重载形式,分别如下所示:
v.erase(pos);   //删除pos位置上的元素
v.erase(begin, end);   //删除[begin, end)区间的数据
insert() 函数与 erase() 函数中的位置参数只能由 begin()、end() 等函数返回的迭代器指示,不能用纯粹的数字。

【示例3】下面通过案例演示这两个函数的用法,C++ 代码如下:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
    vector<char> v;   //创建空的vector容器v
    v.insert(v.begin(), 'a');   //在头部位置插入元素a
    v.insert(v.begin(), 'b');   //在头部位置插入元素b
    v.insert(v.begin(), 'c');   //在头部位置插入元素c
    v.insert(v.begin() + 1, 5, 't');   //在v.begin()+1位置插入5个元素t
    for(int i = 0; i < 8; i++)   //输出容器v中的元素
        cout << v[i] << " ";
    cout << endl;
    cout << "after erase elems:\n"; 
    //删除begin()+1到begin()+6区间的5个元素
    v.erase(v.begin() + 1, v.begin() + 6); 
    for(int i = 0; i <3; i++)   //输出容器v中的元素
        cout << v[i] << " ";
    cout << endl;
    return 0;
}
运行结果:

c t t t t t b a
after erase elems:
c b a

示例分析:
注意,删除区间元素时,区间是左闭右开,即第2个位置上的元素会删除,而第6个位置上的元素不会删除。由图7-6可知,erase()函数删除了5个元素t。

多学一招:deque容器

deque 容器与 vector 容器非常相似,采用动态内存管理的方式存储元素。与 vector 不同的是,deque 是两端开口的,支持从两端插入、删除数据,并支持元素的随机访问。

deque 容器的存储结构如图 2 所示。

图2 deque容器的存储结构
图2 deque容器的存储结构

deque 的操作方法和 vector 容器几乎相同。最大的区别是 deque 容器不支持 vector 容器中的 reserve()函数、capacity() 函数和 data() 函数,并且新增了 pop_front()、push_front() 函数,用于从队首弹出、插入元素。

优秀文章