文章目录
1. vector底层实现原理
一句话概括:底层实现了一个动态数组
1.1 类构成
class vector : protected Vector base
- protected继承:基类的 public 在子类中将变为 protected,其他权限不改变
- 它实现了一个动态的数组,内部包含指向动态分配内存的指针以及记录数组大小和容量的成员变量
_Vector_base
类的数据成员_M_start
:指向容器开始的位置_M_finish
:指向容器的下一个可插入位置_M_end_of_storage
:指向分配的动态内存的尾部
1.2 构造函数
- 无参构造
- 不立即分配内存,而是在添加元素时按需分配,避免无谓的内存浪费。
- STL容器总是性能优先
- 初始化元素个数构造
- 根据传入的元素数量分配内存,避免后续可能的重新分配
- 避免多次申请动态内存,从而影响性能
- 如果知道需要多大的空间,使用这种方法能大大减少动态分配内存的开销
1.3 插入元素
- 插入最后
- 如果有足够的内存空间,则直接插入。如果内存空间不足,则将现有元素复制到新的更大的内存区域,释放原来的内存,然后插入新元素
- 插入不是最后
- 对于非尾部插入,除了要分配可能的新内存之外,还需要将插入点之后的所有元素向后移动一位
1.4 删除元素
- 删除最后一个元素
- 简单地将
_M_finish
向前移动一位,不立即释放内存,这样可以为后续的插入操作保留空间
- 简单地将
- 删除不是最后一个元素
- 待删位置之后元素向前平移一位,
_M_finish
向前移动一位 - 删除元素同样不会释放现有已经申请的内存
- 待删位置之后元素向前平移一位,
1.5 读取元素
- 操作符
[]
:不检查下标的合法性,所以它的效率比at()
高 at()
函数:在返回元素之前检查下标的合法性,如果下标越界,会抛出std::out_of_range
异常- 它们都是返回具体元素的引用
1.6 修改元素
- vector 不支持直接修改某个位置的元素
- 可以通过获取元素的引用,然后直接修改引用的值,来实现对元素的修改
1.7 释放空间
std::vector::shrink_to_fit()
函数:尝试减小容器的容量以适应其大小,以释放未使用的内存(C++11新特性)- 使用交换技巧:创建一个临时的空vector,并与原vector进行交换,从而释放其占用的全部内存
2. vector内存增长机制
2.1 特点
- 内存空间只会增加不会减少:在进行插入操作时,如果当前内存不足以容纳更多的元素,
std::vector
将申请更大的内存空间,但是在删除元素时,它并不会释放已经申请的内存空间 - vector的内存是连续的:这意味着可以通过指针算术来遍历vector的元素
- 不同平台或库实现,内存增长方式可能不同:GCC的实现通常会选择翻倍的方式进行增长,而Visual Studio的实现可能选择1.5倍的方式进行增长
2.2 内存增长特性
- 无参构造,连续插入一个元素,内存增长方式:1、2、4、8、16、32、…,即每次都是上一次的2倍
- 有参构造,连续插入一个元素,内存增长方式:n、2n、4n、…,其中n是初始时分配的元素数量
2.3 内存增长过程
- 申请新的更大的内存空间,大小通常是当前内存空间大小的两倍(具体取决于实现)
- 将原有内存空间中的数据移动(或复制)到新的内存空间中
- 释放原有内存空间
- 在新内存空间的尾部插入新的元素
2.4 内存清理
-
交换一个空的vector:通过创建一个临时的空vector,并与原vector进行交换,可以释放原vector占用的全部内存:
std