文章目录
标准模板库STL
STL,即标准模板库(Standard Template Library),是一些常用数据结构和算法模板的集合,主要由6大组成部分组成。
-
容器(Container)
是一种数据结构, 如list, vector, 和deques,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器。
-
算法(Algorithm)
是用来操作容器中的数据的模板函数。例如,STL用sort()来对一 个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以用于从简单数组到高度复杂容器的任何数据结构上。
-
迭代器(Iterator)
提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。 迭代器就如同一个指针。事实上,C++ 的指针也是一种迭代器。 但是,迭代器也可以是那些定义了
operator*()
以及其他类似于指针的操作符方法的类对象; -
仿函数(Function object)
仿函数又称之为函数对象, 本质就是重载了
operator()
的类。 -
适配器(Adaptor)
简单的说就是一种接口类,专门用来修改现有类的接口,提供一种新的接口,或调用现有的函数来实现所需要的功能。主要包括3中适配器Container Adaptor、Iterator Adaptor、Function Adaptor。
-
空间配制器(Allocator)
为STL提供空间配置的系统。其主要工作包括两部分:
-
对象的创建与销毁;
-
内存的获取与释放。
-
容器
1、顺序容器
I. vector
数据结构:动态数组
特点:
- 元素在内存中连续存储,支持随机访问,且访问效率较高。
- 尾插速度快,但是头插和中间插入非常耗时,为O(n)(删除同理)。
resize和reserve的区别:
reserve(n)
是预留n个元素的空间,如果n大于当前的capacity,则开辟一块更大的空间以容纳至少n个元素。但是,对于多出来的这些空间,reserve
不会对他们进行初始化,对这些区域的访问是非法的。resize(n,val)
修改的是容器当前的size。如果n<size,则将容器当前的size缩小至n,多余元素被释放;如果n>size,则向容器中添加值为val的新元素,直到size为n,对这些元素的访问时合法的。
II. list
数据结构:双向链表
特点:
- 非连续存储,不支持随机访问。
- 在任意位置的插入删除都是O(1)的效率。
- 访问头尾效率O(1),其他为O(n)。
III. deque
数据结构:双端队列(Double End Queue)
deque的中控器存储每个内存块的指针,每个内存块的
cur
记录当前数据的位置,first
和last
记录内存块的起始和末尾。
![image-20220816230531293](https://i-blog.csdnimg.cn/blog_migrate/eeb503d8a86f92c329e60777bfdc8ae3.png)
特点:
- 支持随机访问,但由于不是顺序存储,所以性能较vector差一些。
- 支持头尾的快速插入删除,但是中间的插入删除性能较差。
2、关联容器
I.map/set
数据结构:红黑树
特点:
- map存储键值对,set存储键。
- 增删查改效率为log(n)。
- 节点根据键值进行按序存储。
- 不允许相同的键值出现。
- **map重载了operator[]**用于获取键对应的值。
- 键不可修改,但是键值可以修改。
II.multimap/multiset
数据结构:红黑树
特点:
- multimap存储键值对,multiset存储键。
- 增删查改效率为log(n)。
- 节点根据键值进行按序存储。
- 允许相同的键值出现,但不保证它们之间的相对顺序。
- multimap没有重载operator[],因为键允许重复。
- 键不可修改,但是键值可以修改。
III. unordered_map/unordered_set
数据结构:哈希表
特点:
- unordered_map存储键值对,unordered_set存储键。
- 增删查改的平均时间复杂度为O(1)。
- 键值的存储是无序的。
- 键必须是唯一的,不允许重复。
- unordered_map重载了operator[]用于获取键对应的值。
3、容器适配器
I. stack
数据结构:栈,默认封装deque的接口,封装容器可根据传入的模板参数改变
特点:
- 后进先出
- 只允许从栈顶增删查数据。
II. queue
数据结构:队列,默认封装deque的接口,封装容器可根据传入的模板参数改变
特点:
- 先进先出
- 只允许从队头删查数据,队尾增数据。
III. priority_queue
数据结构:堆,默认封装vector的接口,封装容器可根据传入的模板参数改变
特点:
- 最值存储在堆顶。
- 只允许从堆顶增删查数据,不允许改数据。
容器迭代器失效问题
-
对于顺序容器,如
vector
,删除前面的元素会导致后面元素的迭代器失效,但是可以通过erase
获取到被删除元素的下一个元素的新迭代器。此外,增加元素可能会导致整个容器的扩容,从而使所有迭代器都失效。对于
list
,由于是非顺序存储,因此增删一个节点不会使其他节点的迭代器失效。对于
deque
,如果增加元素导致扩充,则会引起所有迭代器失效,因为迭代其内部有指向中控器的指针。其他情况下,对于头尾的插入删除,不会引起迭代器失效,但是对于中间元素的插入删除,会引起对应位置前或后的元素移动,具体哪些迭代器会失效根据当时的内存分布而定。 -
对于关联容器,如
map/set/multimap/multiset
,底层使用红黑树,是非顺序存储,所以增删一个节点不会使其它迭代器失效。但是它们的erase
不会返回下一个节点的迭代器。但是对于
unordered_map/unordered_set
,底层使用哈希表,增加节点可能会导致整个表的重映射,从而导致所有迭代器失效。
空间配置器
空间配置器是用来实现内存空间分配的工具,与容器联系紧密,每一种容器的空间分配都是通过空间分配器alloctor实现的。
1、空间配置器原理
空间配置器有两级结构,第一级用于处理128字节以上的大内存,其余小内存由第二级处理。
I. 第一级
直接使用malloc和free向系统申请和释放内存。
II. 第二级
维护了一个内存池,内存池中存有16种不同大小的内存块(128/8),用于适应不同的内存需求。
2、空间配置器的优势
- 内存池的介入能够极大地提高内存分配与回收的效率。
- 更方便维护和管理内存块。