STL 是 C++ 标准模板库,可以理解为一个包含数据结构和算法的软件框架,一共有六大组件
- 容器
- 算法
- 迭代器
- 配接器
- 仿函数
- 空间配置器
这篇文章主要总结序列式容器,下一篇总结关联式容器
常用的序列式容器有 vector、list、deque、string
vector
是一个可变大小的序列式容器,其底层结构是动态的数组,在堆中分配连续的内存空间来存储元素,它和数组不同之处就是,数组一般是静态的,大小是固定的,而 vector 的容量大小会随着有效元素的增加而变大
vector 同时也具有数组的特性,例如:
- O(1) 时间的快速访问
- 插入和删除的时间复杂度为O(N)
vector 的扩容规则 分为两种情况
-
如果后面内存够用的话,直接在原来的位置扩容,在 VS 编译器是以 2 倍的扩容方式
-
如果后面内存不够用,则操作系统会在内存的另一块区域寻找更大的内存空间,然后进行拷贝元素,最后释放原来的空间,所以 vector 在扩容的时候,会引发迭代器失效的问题,而这种扩容效率也比较低效
vector 迭代器
迭代器(iterator)就相当于是一个工具,当然也可以把它理解为指针,但又不完全是指针,迭代器是指针的泛指,因为迭代器有很多种,比如Java和 C# 都有他们自己的迭代器;C++ 中的迭代器是一种检查容器内元素并遍历元素的数据类型
使用案例
int myints[] = {
16,2,77,29 };
int n = sizeof(myints) / sizeof(myints[0]);
std::vector<int> fifth(myints, myints +n);
// 写法一:定义迭代器,名字是 it
std::vector<int>::iterator it = fifth.begin();
// 写法二:auto it = fifth.begin()
for (; it != fifth.end(); ++it) {
std::cout << *it<<' ';
}
std::cout << '\n';
当然一个容器的迭代器有可能会有很多种类,比如,正向,反向,const 类型的迭代器
// 无法修改的 const 迭代器
std::vector<int>::const_iterator it = fifth.cbegin();
逆向迭代器
std::vector<int>::reverse_iterator it = fifth.rbegin();
迭代器的失效问题(重点)
什么是迭代器失效,就是对容器的一些操作影响了元素的位置
(1) 删除 pos 位置的数据会导致 pos 迭代器失效,以及 pos 位置后面的迭代器也将失效
例如 1 2 3 4 5 6 7 8 9 ,假如此时迭代器指向6,那我们把 6 元素删除后,vector 中 6 后面的元素会依次向前走, 此时迭代器指向的时候 7 元素了,但是如果还对迭代器进行 ++ 操作,就会出错,因为被操作的迭代器是指向 6 的,它已经被删除了,正确做法是让 erase 重新赋值到指向 7 的迭代器,因为每删除一个元素,erase 自动返回指向下一个位置的迭代器
迭代器失效,错误案列
int a[] = {
1, 2, 3, 4 };
vector<int> v(a, a + sizeof(a) / sizeof(int));
// 实现删除v中的所有偶数
// 下面的程序会崩溃掉,如果是偶数,erase导致it失效
// 对失效的迭代器进行++it,会导致程序崩溃
vector<