《STL源码剖析》要点摘抄

1. STL的空间配置器

  SGI STL设计了双层级配置器,第一级配置器直接使用malloc()、free(),第二级配置器则视情况采用不同的策略:当配置区块超过128bytes时,视为“足够大”,便调用第一级配置器;当配置区块小于128bytes时,视为“过小”,为降低额外负担,便采用复杂的memory pool整理方式,不再求助于第一级配置器。第二级配置器维护16个自由链表,负责16种小型区块的次配置能力。内存池以malloc()配置而得。如果内存不足,转调用第一级配置器。

  设计“内存不足处理例程”是客户端的责任。如果客户端没有设计,则STL直接丢出bad_alloc异常,或利用exit(1)直接退出程序。

  当第二级配置器配置区块时,会将要配置的小额区块的内存需求量上调至8的倍数。当发现自由链表(free list)中没有可用区块时,就调用refill(),缺省取得20个新节点。

2. STL的迭代器

  迭代器(iterator):扮演容器与算法之间的胶合剂,是所谓的“泛型指针”,迭代器必须实现operator*, operator->, operator++, operator--。因为只有容器设计者知道该如何遍历自己的元素,所以STL并没有将迭代器单独列出来以满足所有的容器。

  偏特化(Partial Specialization),意思是如果class template拥有一个以上的template参数,可以针对其中某个(或数个,但非全部)template参数进行特化工作。(另一种解释是针对任何template参数更进一步的条件限制所设计出来的一个特化版本)。

  Quick Sort的泛型算法一定要求迭代器是RandomAccessIterators。因为任何一个元素都可以被选作枢轴(pivot),但是其合适与否却会影响Quick Sort的效率。为了避免枢轴不够随机带来的恶化效应,最理想的方式是取整个序列的头、尾、中央三个位置的元素,以其中值作为枢轴,这种做法成为三点中值(Median-of-Three),为了能够快速取出中央位置的元素,显然迭代器必须能够随机定位,因此快速排序的泛型算法中迭代器必须是RandomAccessIterators。

3. 序列式容器

  vector维护的是一个连续线性空间(可以理解为数组),支持随机存取,因此提供的迭代器是RandomAccessIterators。增加新元素时,如果超过当时的容量,则容量会扩充至两倍。所谓的动态增加大小,并不是在原空间之后接续新空间,而是以原大小的两倍另外配置一块空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效了。

  list封装了链表。SGI STL的list是一个双向链表(double linked-list),迭代器必须具备前移和后移的能力,所以list提供的是Bidirectional Iterator(注意,list不支持随机存取,所以提供的不是RandomAccessIterators)。list的删除操作只有“指向被删除元素”的那个迭代器失效,其他迭代器不受影响。

  list和vector的最主要的区别在于vector使用连续内存存储的,它支持[]运算符,而list是以链表形式实现的,不支持[]。vector对于随机访问的速度很快,但是对于插入尤其是在头部插入元素速度很慢,在尾部插入速度很快。list对于随机访问速度慢得多,因为可能要遍历整个链表才能做到,但是对于插入就快的多了,不需要拷贝和移动数据,只需要改变指针的指向就可以了。另外对于新添加的元素,vector有一套算法,而list可以任意加入。

  vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。vector当空间不足时,是另外分配一块空间,拷贝原有内容,删除旧空间;而deque是另外分配一块空间,但是deque的接口提供的还是连续的接口,显然deque内部的迭代器做了很多工作,使得deque至少看起来是连续线性的。

  stack是封装的deque,换句话说stack实际上是一种配接器。配接器是对内部物的某些接口的封装,不提供内部物的其他接口的访问,与继承等有明显不同。

4. 关联式容器

  map,set属于标准关联容器,使用了非常高效的平衡搜索二叉树:红黑树(RB-tree),它的插入删除效率比其他序列容器高是因为不需要做内存拷贝和内存移动,而直接替换指向节点的指针即可。平衡二叉树有许多种类型,包括AVL-tree、RB-tree等。

  二叉搜索树(binary search tree),又称二叉查找树,二叉排序树,可提供对数时间的元素插入和访问。二叉搜索树的节点放置规则是:任何节点的键值一定大于其左子树中每一个节点的键值,并小于其右子树中的每一个节点的键值。因此,从根节点一直往左走,直至无左路可走,即得最小元素;相反,即得最大元素。

  注意,二叉搜索树的插入和删除常作为面试题。删除操作分为两种情况,一种是被删除节点只有一个子节点,直接将该子节点替换被删除节点的父节点即可;第二种是被删除节点的左右子节点都在,则将右子树中的最小值替换被删除节点即可。

  set自动排序,不能直接修改元素,需要先删除再插入,不能重复,multiset可以。set插入删除元素后,原有的除了被删除节点迭代器的其他迭代器不失效。set之所以不能直接修改元素值,是因为set的元素值就是其键值,关系到set元素的自动排序规则。

  map会根据元素的键值自动排序。map的所有元素都是pair,同时拥有键值(key)和实值(value)。pair的第一元素称为键值,第二个元素称为实值。map的键值不允许修改。map不允许两个元素拥有相同的键值。

  set和vector的区别在于set不包含重复的数据。set和map的区别在于set只含有key,而map有一个key和key所对应的value两个元素。map和hash_map的区别是hash_map使用了hash算法来加快查找过程,但是需要更多的内存来存放这些hash元素,因此可以算得上是采用空间来换取时间策略。

 

原文地址:https://www.cnblogs.com/jiayayao/p/6551238.html