C++ vector介绍

1. vector简介

stl(标准模板库)中有六大组件:容器、迭代器、适配器、分配器、算法和函数对象。接下来我们要介绍的就是vector容器。

向量(vector),是一个封装了动态数组大小的顺序容器(Sequence Container)。相较于静态数组(array),vector可以进行扩容。

C++中将vector实现为类模板,具体使用时需要模板实例化。vector底层的物理结构是动态数组,因此vector中所存数据是占用连续内存的。

在这里插入图片描述

2. vector相关接口介绍

在介绍vector相关接口前,我们必须弄清楚vector底层的实现原理,即vector是通过哪些成员变量实现的。
在stl中,vector是通过三个迭代器实现的,而这三个迭代器实质上就是指针类型,如下所示:

在这里插入图片描述

2.1 构造函数

以下是C++11版本下的vector相关构造:

在这里插入图片描述

1、默认构造
默认构造不需要进行任何的传参,一般来说,默认构造将三个指针全部初始化为空指针即可。

2、利用迭代器构造
利用迭代器构造写成模板的形式,因为不同的容器,迭代器类型不同。迭代器构造使得我们可以用别的容器来初始化vector

3、拷贝构造
vector的底层实现为一个动态数组,有资源的申请,因此拷贝构造肯定是深拷贝。

4、用初始化列表初始化
initializer_list是C++11中引入的一个新的模板类,呈现形式就是两个大括号括起来一些数据,这就是初始化列表。vector容器用初始化列表初始化,就像C语言中普通的数组初始化一样,较为方便。

在这里插入图片描述

2.2 迭代器

//vector模板类的整体架构
template<class T>
class vector
{
public:
typedef T* iterator;//对应普通vector对象
typedef const T* const_iterator;//对应const vector对象
...
private:
...
}

迭代器的分类:单向迭代器、双向迭代器和随机迭代器,这涉及到一个父子类、继承的知识,当然迭代器也可以分为普通迭代器和反向迭代器。继承关系具体如下所示:
在这里插入图片描述
C++的stl中为什么要搞这么一个迭代器呢?最主要的目的是为了实现容器与算法的解耦,算法设计成函数模板,参数设置为迭代器,这样对于不同的容器,都可以使用同一种算法,而不需要对于每一种容器都涉及一种算法。这种解耦是一种很高级的设计思想。

2.3 容量相关接口

在这里插入图片描述
max_size:这个参数是指vector对象最大能存储的数目,一般与系统有关,不用太关心

resize & reserve:这两个都是用于扩容的。对于resize,当所给参数小于实际数据个数时,会将多余的数据清理,但不会缩容;对于reserve,一定能实现扩容,是否能实现缩容是不确定的。

2.4 访问相关接口

在这里插入图片描述
operator[] & at : 这两个都是用于访问数据,区别在于[]不做边界检查,而at会做边界检查,如果越界,则抛出out_of_range的异常。所以,[]访问速度略快于at,但是at的访问会更加安全。

front & back :即获取vector开头与末尾的数据

data:这个接口是C++11中新增的,是返回指向vector第一个元素的指针,实质上是返回vector的成员变量begin(类型为iterator)

2.5 vector修改

在这里插入图片描述
上述接口中,使用最多主要是push_back/pop_back/insert/erase/clear/swap

push_back & pop_back:尾插和尾删。

insert & erase:指定位置的插入和删除。由于vector的存储特性,insert和erase往往需要挪动数据,因此慎用。

clear:清空所有数据,但是不缩容。

swap:用于交换两个vector对象中的数据,实际上只需要交换两个vector对象的三个成员变量即可。

3. 迭代器失效

迭代器失效的定义:

在C++中,迭代器失效(iterator invalidation)是指在容器发生某些操作后,之前创建的迭代器变得不可用或不可靠。这种情况可能会导致未定义行为(undefined behavior),因此需要小心处理。

对于迭代器失效,不同的编译器下的处理不同,在VS2022中,对于失效的迭代器,不允许访问,强行访问程序会出错。但对于其它编译器,比如说g++,就没有这么严苛,失效的迭代器也可以正常使用,不过这极有可能出现未预测的行为。因此,失效的迭代器,我们统一不再使用。

那么,在什么情况下,会导致迭代器失效呢?
以下,我们以vector容器为例,进行讲解。

1、扩容带来的迭代器失效:对于异地扩容而言,在异地扩容之前初始化的迭代器所指向的空间已被释放,因此这些迭代器均会失效,实际上成了野指针

2、insert带来的迭代器失效:insert插入时带来扩容,这是第一种情况;在未扩容时,指向insert插入位置及其之后元素的迭代器会全部失效,因为一旦插入新数据后,元素后移,这些迭代器指向的数据会全部改变。

3、erase带来的迭代器失效:erase删除时,会带来元素前移,类似insert,指向erase删除位置及其之后位置的迭代器均会失效。

那么,我们该如何处理失效的迭代器呢?
两种方法:要么不再使用,要么对失效的迭代器进行更新。

不再使用好理解,我们如何更新失效的迭代器呢?答案是利用insert和erase的返回值。

在这里插入图片描述

在这里插入图片描述

我们可以看到,insert和erase的任意一个重载都有一个iterator类型的返回值。对于insert,返回指向插入数据中第一个数据的迭代器;对于erase,返回指向删除数据中的最后一个数据的后一个数的迭代器

具体内容如下所示:

insert return value:An iterator that points to the first of the newly inserted elements.

erase return value: An iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.

4. 使用vector实现动态二维数组

借由一道经典的算法题目来讲解vector实现动态二维数组。

给定一个非负整数 numRows,生成「杨辉三角」的前 numRows 行。
OJ链接

如果这道题用C语言来实现的话,会比较麻烦,因为要使用二级指针,但是用vector就会很方便,从其中我们也可以看出vector作为类模板的强大之处。

vector<vector<int>> vv;//这样即可实现一个动态二维数组,即vv这个对象中的每一个元素都是一个类型为vector<int>的对象

//正解代码如下
class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv(numRows,vector<int>());
        for(size_t i = 0;i<numRows;i++)
        {
            vv[i].resize(i+1,1);
        }
        for(size_t i = 2;i<numRows;i++)
        {
            for(size_t j = 1;j<vv[i].size()-1;j++)
            {
                vv[i][j] = vv[i-1][j-1]+vv[i-1][j];
            }
        }
        return vv;
    }
};
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值