【C++初阶】vector

⭐博客主页:️CS semi主页
⭐欢迎关注:点赞收藏+留言
⭐系列专栏:C++初阶
⭐代码仓库:C++初阶
家人们更新不易,你们的点赞和关注对我而言十分重要,友友们麻烦多多点赞+关注,你们的支持是我创作最大的动力,欢迎友友们私信提问,家人们不要忘记点赞收藏+关注哦!!!


前言

vector是一个和string类似,但比string高级又难理解的新的知识,我们本次仅仅讲解vector的使用,而模拟实现身临其境一下我将会在接下来的章节中进行重新讲解。


一、vector的介绍

  1. vector是表示可变大小数组的序列容器。
  2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
  3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
  4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
  5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
  6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。

vector的简单介绍

在这里插入图片描述


二、 vector的使用

在这里插入图片描述

1、constructor

在这里插入图片描述

(1)无参构造

在这里插入图片描述

(2)n个val值进行初始化

在这里插入图片描述

(3)迭代器区间构造

在这里插入图片描述

在这里插入图片描述
我们敲一下string代码并将其赋给vector试一下:
在这里插入图片描述

(4)拷贝

在这里插入图片描述

2、destructor

在这里插入图片描述

3、Iterator

在这里插入图片描述

(1)正向迭代器

在这里插入图片描述

(2)反向迭代器

在这里插入图片描述

(3)const正向 / 反向迭代器

在这里插入图片描述

在这里插入图片描述

4、Capacity

在这里插入图片描述
我们在string已经讲过这些函数的接口,这里的vector与string的实现方式大同小异,我们就不细讲,下面我们讲一些不同的类型。

(1)size&&max_size

在这里插入图片描述

在这里插入图片描述

(2)resize 和 capacity

开空间并初始化值:
在这里插入图片描述
看一下空间的容量:
在这里插入图片描述

如下图,开辟10个为1的空间。
在这里插入图片描述

(3)empty

判断这个vector的size是否为空:
在这里插入图片描述

(4)reserve

在这里插入图片描述
在这里插入图片描述

(5)shrink_to_fit

缩容到合适的容量。
在这里插入图片描述
在这里插入图片描述

(6)扩容 ---- 重点

capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义的。vs是PJ版本STL,g++是SGI版本STL。
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问题。
resize在开空间的同时还会进行初始化,影响size。

vs下:
在这里插入图片描述
Linux下:
在这里插入图片描述

5、Element access

在这里插入图片描述

(1)operator[]

在这里插入图片描述

在这里插入图片描述

(2)front && back

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

6、Modifiers

在这里插入图片描述

(1)assign

在这里插入图片描述

在这里插入图片描述

(2)push_back && pop_back

在这里插入图片描述

(3)insert

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

(4)erase

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

(5)swap

在这里插入图片描述

在这里插入图片描述

(6)clear

在这里插入图片描述
在这里插入图片描述

(7)find

在这里插入图片描述

在这里插入图片描述


三、vector<>

1、解释

我们在前面讲了那么多的vector发现和string怎么那么相似呢,那为什么string不用<>而vector要用<>呢?我们接下来详细讲一下这个<>的秒用。

<>内部加上我们的类型发现可以接收不同的类型,怎么越看越像类模板,当然,它就是一个类模板。
我们先简单画个图:
在这里插入图片描述

看起来是不是很妙,是用的同一个模板去实例化,<>中可以放不同的类型,甚至是可以放自己本身,与数组有本质的区别,在存放自定义类型和内置类型的存放逻辑是一样的,只能说用法和数组类似,但底层的实现下完全不同。

2、题目

杨辉三角oj题

class Solution {
public:
    vector<vector<int>> generate(int numRows) {
        vector<vector<int>> vv;
        vv.resize(numRows, vector<int>());
        size_t i = 0;
        for(i = 0; i < vv.size(); ++i)
        {
            vv[i].resize(i+1, 0);
            vv[i][0] = vv[i][vv[i].size()-1] = 1;
        }
        for(i = 0; i < vv.size(); i++)
        {
            for(size_t j = 0; j < vv[i].size(); ++j)
            {
                if(vv[i][j] == 0)
                {
                    vv[i][j] = vv[i-1][j] + vv[i-1][j-1];
                }
            }
        }
        return vv;
    }
};

代码解释:
在这里插入图片描述

电话号码的字母组合

在这里插入图片描述

我们画一下递归展开图解释一下吧:
在这里插入图片描述

在这里插入图片描述


四、重点:迭代器的失效问题

1、insert迭代器失效问题

在这里插入图片描述

看起来很正常的样子,我们进行测试一下不同的数据:
在这里插入图片描述

我们可以重新看一下push_back的代码:
在这里插入图片描述
有扩容的,我们就可以理解一个很深刻的问题,我们如果在main函数中的push_back很多值,则会进行扩容到有容量的地方,则insert函数内是不会进入扩容的,所以我们的pos依旧用的是原本的空间,因为我们知道,迭代器的扩容是将原本空间存放到新的空间,再释放掉原本的空间,我们利用调试来看一下为什么这个iterator会变成野指针了。可以理解为_start,_finish,_end_of_storage都跟着扩容的新空间进行跑路了,而只有傻傻的pos留在原空间,成为野指针。

在这里插入图片描述

我们看一下我们的画图解释:这是在insert函数内部进行扩容的逻辑。
在这里插入图片描述
解决方法:在扩容后更新一下迭代器的位置。

在这里插入图片描述
在这里插入图片描述

2、insert在main和insert

在这里插入图片描述
insert后面没有返回值以后没有返回iterator以后我们认为pos失效了,不能在使用了。

我们走入调试看一下:

在这里插入图片描述

解决方法:

在这里插入图片描述

调试走一下:

在这里插入图片描述

在这里插入图片描述

3、erase在linux的g++情况下和在vs情况下迭代器失效问题的不同

这段较为长,我们细细来讲解。

vs下:
首先我们先写一下erase函数:

在这里插入图片描述
我们进入一下测试,发现很不错,很香,和我们的顺序表一样,都是可以的:
在这里插入图片描述

可是事实真是这样吗?我们看一下库函数erase的实现吧:

在这里插入图片描述

发现怎么会这样报错呢?迭代器是失效了,说明我们的代码是错误的,我们走一下linux下g++来看一看吧。

linux下:
发现没问题啊!
在这里插入图片描述

接下来我们思考一个问题,如果我们想要删除的在最后那个位置呢(也就是尾删)?我们在vs下走自己写的和库函数一下一看,以及我们在linux下跑的代码:
在这里插入图片描述
在这里插入图片描述

我们引入一个话题,我们做一个要求删除所有的偶数的小练习:

vs库函数下:
vs库函数下实现用的是封装的指针。
在这里插入图片描述

vs自己实现的:
因为我们自己实现的erase函数是用的是原生指针。
在这里插入图片描述

linux下:
在这里插入图片描述
linux下看似没问题,我们改成1234:
在这里插入图片描述

在这里插入图片描述

我们想一想一个深层的东西,如果仅仅是将pos改成小于号呢?逻辑上是成立的(这里我们就不演示了),但是我们再用一个连续偶数的例子来打破我们的思维:
在这里插入图片描述

我们画一下图来进行演示一下:
在这里插入图片描述

解决方法(加上返回值):

这是库里面实现的:
在这里插入图片描述
在这里插入图片描述

vs下:
在这里插入图片描述

linux下:
在这里插入图片描述

结论

我们的linux的g++下的关于迭代器不更新以后导致的错误是编译器不进行检查,具体问题具体分析,会出现错误;而我们的vs编译器会进行强制检查迭代器是否进行更新,如果未及时更新,那么编译器直接强制报错,所以我们最终加了一个iterator的返回值,使迭代器每进行一次erase就更新一次,这样才不会导致出错。

4、与vector类似,string在插入+扩容操作+erase之后,迭代器也会失效

但我们的string不经常用迭代器,更多的是用下标,所以我们的string用到迭代器失效问题很少。


家人们不要忘记点赞收藏+关注哦!!!

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

2022horse

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值