目录
🚀0.前言
言C++之言,聊C++之识,以C++会友,共向远方。各位博友的各位你们好啊,这里是持续分享C++知识的小赵同学,今天要分享的C++知识是vector ,在这一章,小赵将会向大家聊聊C++的vector知识 。✊
我们先来看一下关于vector的介绍,也方面我们后面更好的使用它
1. vector是表示可变大小数组的序列容器。(这个很香,动态的数组)
2. 就像数组一样,vector也采用的连续存储空间来存储元素。也就是意味着可以采用下标对vector的元素 进行访问,和数组一样高效。但是又不像数组,它的大小是可以动态改变的,而且它的大小会被容器自动处理。
3. 本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。(这里vector每次使用时候都会按一定的数据量开一定的空间,当快要满时,再以一定的倍数扩充空间)
4. vector分配空间策略:vector会分配一些额外的空间以适应可能的增长,因为存储空间比实际需要的存储空间更大。不同的库采用不同的策略权衡空间的使用和重新分配。但是无论如何,重新分配都应该是对数增长的间隔大小,以至于在末尾插入一个元素的时候是在常数时间的复杂度完成的。
5. 因此,vector占用了更多的存储空间,为了获得管理存储空间的能力,并且以一种有效的方式动态增长。
6. 与其它动态序列容器相比(deque, list and forward_list), vector在访问元素的时候更加高效,在末尾添加和删除元素相对高效。对于其它不在末尾的删除和插入操作,效率更低。比起list和forward_list统一的迭代器和引用更好。
🚈1.vector 构造函数
🚝 1.1vector构造函数
vector的构造函数的实现和string稍有不同,内部实现中带了模板,如果你要设定vector里面存储什么元素类型只需要像下面这样设计。
vector<int>a;//a里面的元素类型为int
vector<string>b;//b里面的元素类型为string
vector<char>c;//c里面的元素类型为char
vector<vector<int>>d;//d里面的元素类型为vector<int>,这也就是二维数组
而像这样没有给任何一个元素的构造,也就是使用的默认构造函数,里面默认时没有元素的。
而除了这种默认构造的方式,我们还有几种常用的构造方式。
vector<int>a(5.1);//a里面为5个元素为1
vector<int>b(5);//b里面的元素为5个0
vector<int>c(a.begin(), a.end());//用a构造c(迭代器的方式)
string s = "abcdef";
vector<char>d(s.begin(), s.end());//用s里面的字符构造d
以上的这几种构造方式就是我们常用的方法,当然C++11引入{}后,我们也可以用{}去构建
vector<int>e{ 1,2,3,4,5 };//和我们之前学的C的数组一样
vector<int>f={ 1,2,3,4,5 };
🚝1.2vector拷贝构造函数
vector<int>a={ 1,2,3,4,5 };
vector<int>b(a);//用a构造b
大家这里有了前面string的经验使用起来应该不难
除了可以这样拷贝构造还可以这样
vector<int>c = a;
这里面可以查看一下代码的反汇编,就能看出来这个是不是我们的拷贝构造函数
我们发现在底层用的都是拷贝构造函数,也就是我们以后用拷贝构造函数可以这样用
🚝补充:operator=
讲完了拷贝构造的=的使用就不可避免的要与我们vector里面重载的赋值=去做一个区分了。
拷贝构造的=,故名思意是给没有初始化,尚未调用过我们的构造函数,去构造对象的。而我们重载的赋值=则是将另一个类里面的值赋值到另一个里面。(就是复制过来)
我们尝试调用了一下vector的=,发现并没有出现内存报错,说明里面的实现机制和我们的深拷贝类似。我们自己在实现这一块的时候也要主要,内存要自己去开,然后把值进行拷贝。
🚈2.vector类的使用
🚝 2.1迭代器
vector的迭代器实现类型的很多
2.1.1非const迭代器
vector的迭代器在我们熟悉的begin和end后面加上了rbegin和rend也就是反向迭代器,就是rbegin==end,rend==begin。 使用起来和begin和end差不多。
当然还有一个比较有意思的点这里的++也应该是进行了重载,类似像是--。
用下面的图片来方便大家对于反向迭代的理解。
2.2.2const迭代器
vector的内部除了实现了反向迭代器,还实现了const的迭代器。
和我们之前在int前面加const一样,加一个const在前面这个迭代器内部的值就不能被修改了。
这里我们试图去修改内部的值,很明显报了错误。
🚝2.2vector的空间函数
首先我们来看一下不同平台对于vector的空间扩容方案:
capacity的代码在vs和g++下分别运行会发现,vs下capacity是按1.5倍增长的,g++是按2倍增长的。 这个问题经常会考察,不要固化的认为,vector增容都是2倍,具体增长多少是根据具体的需求定义 的。vs是PJ版本STL,g++是SGI版本STL。
2.2.1vector::size
这个和string的一样获取元素的个数,后面遇到类似的类也是一样,后面就不说这个函数了。
2.2.2vector::capacity
也是一样获取目前vector开的空间
2.2.3empty
判断vector里面的元素数量是否为空,如果为空则返回true,不然返回false;
2.2.4reserve
reserve只负责开辟空间,如果确定知道需要用多少空间,reserve可以缓解vector增容的代价缺陷问 题。
2.2.5resize
resize在开空间的同时还会进行初始化(0),影响size。
2.2.6operator[](像数组一样访问)
vector类也重载了[],这样就可以像访问数组一样访问我们的vector类了很香。
🚈3.增删查改等
3.1push_back尾插
3.2pop_back尾删
3.3swap(交换两个vector的数据空间)
这个就是交换两个数组的元素,这个接口并不常用,了解就行。
3.4insert(在position之前插入val)
vector<int>a{ 1,2,3,4,5 };
vector<int>::iterator it=a.begin()+1;
a.insert(it, 5);//迭代器
这里要注意的是vector用的迭代器进行插入
3.5erase(删除position位置的数据 )
特别补充:迭代器失效问题
迭代器失效问题的原因是从哪里来的呢,其实就在我们介绍vector类的时候就有伏笔。
在我们前面的时候我们说vector在开空间的时候,它是重新分配一个数组,也就是下面这段。
本质讲,vector使用动态分配数组来存储它的元素。当新元素插入时候,这个数组需要被重新分配大小为了增加存储空间。其做法是,分配一个新的数组,然后将全部元素移到这个数组。就时间而言,这是 一个相对代价高的任务,因为每当一个新的元素加入到容器的时候,vector并不会每次都重新分配大小。
这段话其实翻译过来就是vector会重新开一个空间,然后就原本的数组元素拷贝过去。
而我们的迭代器用的指针,而我们新开的数组是新的地址,而之前的迭代器指向的是老数组,那么这个时候我们的迭代器就会出现失效的问题。而解决办法就是获取新的迭代器。
迭代器失效图片
另外除了insert扩容的问题,还要注意erase之后迭代器如果指向最后一个位置被删除,那么可能也会出现内存错误,访问野指针的问题。
💎4.结束语
好了小赵今天的分享就到这里了,如果大家有什么不明白的地方可以在小赵的下方留言哦,同时如果小赵的博客中有什么地方不对也希望得到大家的指点,谢谢各位家人们的支持。你们的支持是小赵创作的动力,加油。
如果觉得文章对你有帮助的话,还请点赞,关注,收藏支持小赵,如有不足还请指点,方便小赵及时改正,感谢大家支持!!!