理解STL之Vector容器
来源:互联网 发布:下载软件 英文 编辑:程序博客网 时间:2024/06/01 20:48
C++内置了数组的类型,在使用数组的时候,必须指定数组的长度,一旦配置了就不能改变了,通常我们的做法是:尽量配置一个大的空间,以免不够用,这样做的缺点是比较浪费空间,预估空间不当会引起很多不便。
STL实现了一个Vector容器,该容器就是来改善数组的缺点。vector是一个动态空间,随着元素的加入,它的内部机制会自行扩充以容纳新元素。因此,vector的运用对于内存的合理利用与运用的灵活性有很大的帮助,再也不必因为害怕空间不足而一开始就配置一个大容量数组了,vector是用多少就分配多少。
要想实现动态分配数组,Vector内部就需要对空间控制做到有效率的掌控,这些机制要如何运作才能高效地实现动态分配呢?本篇博客就从源代码的角度带你领略一下Vector容器内部的构造艺术。
Vector概述
大家知道,初始化一个数组的时候,需要给数组分配一块内存,数组中的数据都是按序存放的。vector也是如此,再初始化的时候给vector容器分配一块内存,用来存放容器中的数据,一旦分配的内存不足以存放新加入的数据时,就需要扩充空间。STLVector的做法是:重新开辟一段新的空间,将原空间的数据迁移过去,然后新加入的数据存放在新空间之后并释放掉原有空间。
在这个过程中,配置新空间->数据移动->释放旧空间会带来一定的时间成本,所以必须尽可能高效的实现,STL的Vector设计中对这一部分做了相当大的优化,使得时间成本尽可能的小。下面就一起去看看这些优秀的设计吧↓。
Vector的数据结构
我们从最简单的开始,Vector的数据结构相当简单,由于需要判断内存是否够用,所以要用到三个指针,分别指向头,目前使用空间的尾,目前可用空间的尾。其源代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
每当初始化一个vector的时候,先分配一段内存,称为目前可用空间,大小为end_of_storage - start + 1,当往vector里面加入数据的时候,finish就往后移,代表目前已使用的空间,这样做的好处是,不用频繁的扩充空间和转移数据,使得时间成本下降。
在上述代码中,我们看到vector采用了STL标准的空间配置其接口,关于空间配置器的知识在带你深入理解STL之空间配置器(思维导图+源码)一文中有讲解,如有疑惑,可以跳转复习一下再来!
vector提供了如下函数来支持获取其数据结构中的相关参数。
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
Vector的迭代器
既然是STL的容器,必须要满足迭代器的相关要求,如对迭代器有疑惑的,参考带你深入理解STL之迭代器和Traits技法
。
vector维护的是一段连续的内存空间,所以不论容器中元素的型别为何,普通指针都可以作为vector的迭代器而满足所有必要的条件。vector支持随机存取,所以vector提供的是Random Access Iterator。
下面来看看vector关于迭代器的源码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
vector的构造函数
默认构造函数
在使用vector的时候,我们通常会有如下定义:
- 1
- 2
- 3
- 1
- 2
- 3
在上述定义中,调用了vector的默认构造函数,其默认不分配内存空间,如下:
- 1
- 2
- 1
- 2
带参构造函数
通常,vector的初始化可以指定元素个数和初始化类型。如下:
- 1
- 1
vector提供下面的构造函数以支持上述初始化操作:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
这里面调用了uninitialized_fill_n函数,这个函数是STL的内存基本处理函数,存放在stl_uninitialized.h中,下面来看看它的源码:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
vector的元素操作函数
push_back()
push_back()函数将新元素插入于vector的尾部,该函数再完成这一操作的时候,先检查是否还有备用空间,如果有直接再备用空间上构造函数;如果没有就扩充空间,通过重新配置一块大空间,移动数据,释放原空间的操作来完成push_back操作。其源代码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 94
- 95
- 96
- 97
- 98
- 99
- 100
- 101
- 102
- 103
- 104
pop_back()函数
pop_back函数弹出当前尾端元素。其源代码比较简单,如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 1
- 2
- 3
- 4
- 5
- 6
- 7
erase()函数
erase函数支持两个版本:
- 清除某个位置上的元素
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 清除某个区间上的所有函数
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这里放上两张《STL源码剖析》中的图,便于理解这一过程:
有上述erase函数,可以衍生出一个函数,用来清除迭代器中所有的元素
- 1
- 1
insert()函数
insert函数实现的功能是:从position开始,插入n个元素,元素的初值均为x。其源码如下:
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
- 92
- 93
上述操作可以使插入操作达到最高的效率。配合以下图解更容易理解:
插入点之后的现有元素个数大于新增元素个数的情况
插入点之后的现有元素个数小于新增元素个数的情况
剩下的内存不够分配,重新配置的情况
后记
STL的Vector容器到此就介绍完毕了。其中对改善效率作了不少优化,很多设计点都值得学习!若有疑惑可以在博文下方留言,我看到会及时帮大家解答!
参考:
- C++ STL源码剖析
- STL源码剖析——迭代器
- 侯捷先生的《STL源码剖析》 华中科技大学出版社
- 理解STL之Vector容器
- STL容器之vector
- STL容器之 vector
- STL之vector容器
- STL容器之vector
- STL容器之vector
- STL 之 vector 容器
- STL之vector容器
- STL容器之vector
- STL容器之vector
- STL之vector容器
- STL之vector容器
- STL容器之Vector
- STL容器之vector
- STL容器之vector
- STL容器 之 vector
- STL容器之vector
- 【STL】STL容器之vector
- Java Web 开发环境与工程搭建
- Redis 下载与搭建(一)
- FileReader和FileWriter使用同一个文件源的问题
- CSAPP:优化程序性能(一)
- EasyRecyclerAdapter
- 理解STL之Vector容器
- ArcSde的两种连接方式、区别
- nginx 13: Permission denied 错误
- Fresco简单的使用—SimpleDraweeView
- 通过优矿API python获取财报
- A Neural Probabilistic Language Model
- Eclipse里项目名有红叉,但是底下的每一个文件都没有红叉
- 算法之路-鸡兔同笼
- page,request,session,application四个域对象的使用及区别