前言
本章主要内容为两个部分:
- vector是什么?
- vector常用接口的使用。
一、vector的介绍
- vector是表示可变大小数组的容器
- 就像数组一样,vector也采用的连续空间来存储元素。也意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
- 本质上,vector使用动态分配数组来存储它的元素。当新元素插入时,这个数组需要被重新分配大小。为了增加存储空间,其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因此每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
- vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因此存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
- 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
- 与其他动态序列容器相比(deque,list and forward_list),vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其他不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
二、vector常用接口的使用
1、vector是一个类模板,使用时需要显示实例化
tip:
- vector有两个模板参数:
- T:元素的数据类型
- Alloc:空间配置器,用于定义存储分配模型的分配器对象的类型。默认情况下,使用allocator类模板,它定义了最简单的内存分配模型,并且与值无关。
- 空间配置器即内存池,STL中所有的容器都使用内存池,因为容器需要频繁申请和释放空间,为了提高效率,所以使用内存池。
- allocator类模板,是库里面实现的一个默认分配器,如果没有指定最后一个模板参数,所有标准容器都将使用这个分配器,它是标准库中唯一的预定义分配器。
- 一般我们都使用库中的这个默认分配器,所以我们不需要显式实例化Alloc。
- 使用类模板,我们必须显式实例化。
- 类模板的显式实例化:类模板名字<实例化的类型>
- 显式模板参数实参与模板参数的匹配:
- 显式模板实参按由左至右的顺序与对应的模板参数匹配
- 第一个模板实参与第一个模板参数匹配,第二个实参与第二个参数匹配,以此类推。
- 注:只有尾部(最右)参数的显式模板实参才可以忽略,但前提是它们可以从函数参数推断出来或为缺省参数。
2、vector的构造函数
(construcort)构造函数声明 | 接口说明 |
---|---|
explicit vector (const allocator_type& alloc = allocator_type()); | 默认构造函数,一般alloc空间配置器不用传参,使用缺省值。即无参构造 |
explicit vector (size_type n, const value_type& val = value_type(),const allocator_type& alloc = allocator_type()); | 构造并初始化n个val |
vector (InputIterator first, InputIterator last,const allocator_type& alloc = allocator_type()); | 使用迭代器进行初始化 |
vector (const vector& x); | 拷贝构造 |
代码示例:
//vector的构造函数
void test_vector1()
{
//1、无参构造
vector<int> v1;//构造一个空的int类型的vector对象
//2、构造并初始化n个val
vector<char> v2(10, 'a');//构造一个char类型的vector,并且初始化10个字符'a'
vector<char> v3(5);//构造一个char类型的vector,并且默认初始化5个字符
//3、使用迭代器初始化
//①使用自己类型的迭代器初始化
vector<char> v4(v2.begin(), v2.end());
//②使用其他类型的迭代器初始化
vector<int> v5(v2.begin(), v2.end());
//③连续存储空间的指针也属于迭代器
int arr[] = {
1, 2, 3 };
vector<int> v6(arr, arr + 3);
//4、拷贝构造
vector<int> v7(v6);
}
tip:
- 无参构造:一般使用最多
- 构造并初始化n个val:
- val为缺省参数,所以可以指定实参(使用指定的实参初始化),或不指定实参(使用缺省值)
- 值初始化: val不指定实参,库会创建一个值初始化的元素初值,把它赋个容器中的元素。这个初值由vector对象中元素的类型决定
- 如果vector对象的元素是内置类型,比如int,则元素默认初始化为0;char,则元素默认初始化为’\0’
- 如果元素是某种类类型,比如string,则元素由类默认初始化
- 使用迭代器初始化构造:
- InputIterator:模板参数,意味着你传什么类型的迭代器,他就实例化什么类型的迭代器初始化构造
- 连续存储空间的指针也是迭代器
- 迭代器区间:左闭合区间,[first,last)
- 拷贝构造:用一个已经存在的对象初始化另一个对象
- 类类型的隐式转换:
- 能通过一个实参调用的构造函数定义了一条从构造函数的参数类型向类类型隐式转换的规则
- 注:每次只能执行一种类类型的转换
- explicit修饰构造函数,禁止隐式类型转换
3、vector的遍历
接口名称 | 接口说明 |
---|---|
operator[] | 下标+[],像数组一样可以随机访问 |
begin+end | 获取第一个元素位置的iterator/const_iterator,获取最后一个元素的下一个位置的iterator/const_iterator |
rbegin+rend | 获取最后一个元素位置的reverse_iterator,获取第一个元素位置前一个位置的reverse_iterator |
范围for | C++11支持的语法糖,只要支持迭代器就可以使用 |
代码示例:
//vector的遍历
void test_vector2()
{
vector<int> v1;
v1.push_back(1);
v1.push_back(2);
v1.push_back(3);
v1.push_back(4);
v1.push_back(5);
//1、operator[]——使用下标+[]
for (size_t i = 0; i < v1.size(); i++)
{
cout << v1[i] << " "