STL六大组件与底层原理

Posted 白龙码~

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了STL六大组件与底层原理相关的知识,希望对你有一定的参考价值。

文章目录

标准模板库STL

STL,即标准模板库(Standard Template Library),是一些常用数据结构和算法模板的集合,主要由6大组成部分组成。

  1. 容器(Container)

    是一种数据结构, 如list, vector, 和deques,以模板类的方法提供。为了访问容器中的数据,可以使用由容器类输出的迭代器。

  2. 算法(Algorithm)

    是用来操作容器中的数据的模板函数。例如,STL用sort()来对一 个vector中的数据进行排序,用find()来搜索一个list中的对象,函数本身与他们操作的数据的结构和类型无关,因此他们可以用于从简单数组到高度复杂容器的任何数据结构上。

  3. 迭代器(Iterator)

    提供了访问容器中对象的方法。例如,可以使用一对迭代器指定list或vector中的一定范围的对象。 迭代器就如同一个指针。事实上,C++ 的指针也是一种迭代器。 但是,迭代器也可以是那些定义了operator*()以及其他类似于指针的操作符方法的类对象;

  4. 仿函数(Function object)

    仿函数又称之为函数对象, 本质就是重载了operator()的类。

  5. 适配器(Adaptor)

    简单的说就是一种接口类,专门用来修改现有类的接口,提供一种新的接口,或调用现有的函数来实现所需要的功能。主要包括3中适配器Container Adaptor、Iterator Adaptor、Function Adaptor

  6. 空间配制器(Allocator)

    为STL提供空间配置的系统。其主要工作包括两部分:

    • 对象的创建与销毁

    • 内存的获取与释放

容器

1、顺序容器

I. vector

数据结构:动态数组

特点:

  1. 元素在内存中连续存储支持随机访问,且访问效率较高。
  2. 尾插速度快,但是头插和中间插入非常耗时,为O(n)(删除同理)。

resize和reserve的区别:

  1. reserve(n)是预留n个元素的空间,如果n大于当前的capacity,则开辟一块更大的空间以容纳至少n个元素。但是,对于多出来的这些空间,reserve不会对他们进行初始化对这些区域的访问是非法的
  2. resize(n,val)修改的是容器当前的size。如果n<size,则将容器当前的size缩小至n多余元素被释放;如果n>size,则向容器中添加值为val的新元素直到size为n对这些元素的访问时合法的

II. list

数据结构:双向链表

特点:

  1. 非连续存储,不支持随机访问
  2. 在任意位置的增删查改都是O(1)的效率

III. deque

数据结构:双端队列(Double End Queue)

deque的中控器存储每个内存块的指针,每个内存块的cur记录当前数据的位置,firstlast记录内存块的起始和末尾。

特点:

  1. 支持随机访问,但由于不是顺序存储,所以性能较vector差一些。
  2. 支持头尾的快速插入删除,但是中间的插入删除性能较差。

2、关联容器

I.map/set

数据结构:红黑树

特点:

  1. map存储键值对,set存储键。
  2. 增删查改效率为log(n)。
  3. 节点根据键值进行按序存储
  4. 不允许相同的键值出现
  5. **map重载了operator[]**用于获取键对应的值。
  6. 键不可修改,但是键值可以修改

II.multimap/multiset

数据结构:红黑树

特点:

  1. multimap存储键值对,multiset存储键。
  2. 增删查改效率为log(n)。
  3. 节点根据键值进行按序存储
  4. 允许相同的键值出现,但不保证它们之间的相对顺序。
  5. multimap没有重载operator[],因为键允许重复。
  6. 键不可修改,但是键值可以修改

III. unordered_map/unordered_set

数据结构:哈希表

特点:

  1. unordered_map存储键值对,unordered_set存储键。
  2. 增删查改的平均时间复杂度为O(1)
  3. 键值的存储是无序的。
  4. 键必须是唯一的,不允许重复。
  5. unordered_map重载了operator[]用于获取键对应的值。

3、容器适配器

I. stack

数据结构:栈,默认封装deque的接口,封装容器可根据传入的模板参数改变

特点:

  1. 后进先出
  2. 只允许从栈顶增删查数据。

II. queue

数据结构:队列,默认封装deque的接口,封装容器可根据传入的模板参数改变

特点:

  1. 先进先出
  2. 只允许从队头删查数据,队尾增数据。

III. priority_queue

数据结构:堆,默认封装vector的接口,封装容器可根据传入的模板参数改变

特点:

  1. 最值存储在堆顶。
  2. 只允许从堆顶增删查数据,不允许改数据

容器迭代器失效问题

  1. 对于顺序容器,如vector,删除前面的元素会导致后面元素的迭代器失效,但是可以通过erase获取到被删除元素的下一个元素的新迭代器。此外,增加元素可能会导致整个容器的扩容,从而使所有迭代器都失效。

    对于list,由于是非顺序存储,因此增删一个节点不会使其他节点的迭代器失效。

    对于deque,如果增加元素导致扩充,则会引起所有迭代器失效,因为迭代其内部有指向中控器的指针。其他情况下,对于头尾的插入删除,不会引起迭代器失效,但是对于中间元素的插入删除,会引起对应位置前或后的元素移动,具体哪些迭代器会失效根据当时的内存分布而定。

  2. 对于关联容器,如map/set/multimap/multiset,底层使用红黑树,是非顺序存储,所以增删一个节点不会使其它迭代器失效。但是它们的erase不会返回下一个节点的迭代器。

    但是对于unordered_map/unordered_set,底层使用哈希表,增加节点可能会导致整个表的重映射,从而导致所有迭代器失效。

空间配置器

空间配置器是用来实现内存空间分配的工具,与容器联系紧密,每一种容器的空间分配都是通过空间分配器alloctor实现的。

1、空间配置器原理

空间配置器有两级结构,第一级用于处理128字节以上的大内存,其余小内存由第二级处理。

I. 第一级

直接使用malloc和free向系统申请和释放内存

II. 第二级

维护了一个内存池,内存池中存有16种不同大小的内存块(128/8),用于适应不同的内存需求。

2、空间配置器的优势

  1. 内存池的介入能够极大地提高内存分配与回收的效率。
  2. 更方便维护和管理内存块。

以上是关于STL六大组件与底层原理的主要内容,如果未能解决你的问题,请参考以下文章

STL六大组件中算法模块sort为啥采用快速排序作为底层思想

STL面试题

STL六大组件之算法

STL六大组件之算法

STL六大组件

5 千字长文+ 30 张图解 | 陪你手撕 STL 空间配置器源码