STL 源码剖析

GitHub参考
STL"源码"剖析-重点知识总结
C++STL自己总结

容器(Containers):各种数据结构,如:vector、list、deque、set、map。用来存放数据。从实现的角度来看,STL容器是一种class template。

序列式容器

所谓序列式容器,其中的元素都可序,但未必有序,C++本身内建了一个序列式容器array,STL另外提供了vector、list、deque、stack、queue、priority-queue等序列式容器。其中stack和queue由于只是deque改头换面而来,技术上被归为一种配接器 (adapter)。

vector

vector 采用的数据结构非常简单:线性连续空间。它以两个迭代器 startfinish 分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器 end_of_storage 指向整块连续空间(含备用空间)的尾端。


注意:所谓动态增加大小,并不是在原来空间之后接续新空间(因为无法保证原空间之后尚有可供分配的空间),而是以原来大小的的两倍另外分配一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素,并释放原空间。因此,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效啦。

list

相对于vector的连续线性空间,list就显得复杂许多,它的好处就是插入或删除一个元素,就配置或删除一个元素空间。
对于任何位置的元素的插入或删除,list永远是常数时间。
list本身和节点是不同的结构,需要分开设计。以下是STL list的节点node结构:

template <class T>
class __list_node {
    typedef void* void_pointer;
    void_pointer prev;
    void_pointer next;
    T data;
};

这是一个双向链表

SGI list不仅是一个双向链表,而且是一个环状双向链表。只需一个指针就可遍历整个链表。

deque

  • deque 和 vector 的最大差异,一在于 deque 允许常数时间内对起头端进行插入或移除操作,二在于 deque 没有所谓容量 (capacity) 概念,因为它是以分段连续空间组合而成,随时可以增加一段新的空间连接起来。
  • deque由一段一段连续空间组成,一旦有必要在deque的前端或尾端增加新空间,便配置一段连续空间,串接在整个deque的前端或尾端。deque的最大任务,便是在这些分段的连续空间上,维护其整体连续的假象,并提供随机存取的接口,避开了“重新配置、复制、释放”的轮回,代价是复杂的迭代器结构。

    deque迭代器
      迭代器首先必须指出分段连续空间在哪里,其次它必须能够判断自己是否已经处在缓冲区的边缘,如果是,一旦前进或后退就必须跳跃下一个缓冲区,为了能够正常跳跃,deque必须随时掌握管控中心。
template <class T, class Ref, class Ptr, size_t BufSiz>
struct __deque_iterator { // 未继承 std::iterator
    // 保持迭代器的连接
    T* cur; // 此迭代器所指之缓冲区的现行( current)元素
    T* first; // 此迭代器所指之缓冲区的的头
    T* last; // 此迭代器所指之缓冲区的的尾(含备用空间)
    map_pointer node; // 指向管控中心
    ...
};

假如deque中已经包含了20个元素了,缓冲区大小为8,则内存布局如下:

stack

  • stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。
  • stack允许增加元素、移除元素、取得最顶端元素。但除了最顶端外,没有任何其他方法可以存取,stack的其他元素,换言之,stack不允许有遍历行为。
  • stack默认以deque为底层容器
  • 由于 stack 系以底部容器完成其所有工作,而具有这种 "修改某物接口,形成另一种风貌" 之性质者,称为 adapter(适配器),因此,STL stack 往往不被归类为 container(容器),而被归类为 container adapter

queue

  • queue是一种先进先出(First In First Out,FIFO)的数据结构,它有两个出口,允许增加元素、移除元素、从最底端加入元素、取得最顶端元素。但除了最底端可以加入、最顶端可以取出外,没有任何其他方法可以存取queue的其他元素,换言之,queue不允许有遍历行为。
  • queue默认以deque为底层容器

heap

priority_queue

slist

关联式容器

标准的 STL 关联式容器分为 set (集合) 和 map (映射表) 两大类,以及这两大类的衍生体 multiset (多键集合) 和 multimap (多键映射表) 。这些容器的底层机制均以 RB-tree (红黑树) 完成
此外,SGI STL 还提供了一个不在标准规格之列的关联式容器:hash table (散列表),以及以此 hash table 为底层机制而完成的 hash_set (散列集合)、hash_map (散列映射表)、hash_multiset (散列多键集合)、hash_multimap (散列多键映射表)

set

  • set的所有元素都会根据元素的键值自动排序。
  • set的元素不像map那样可以同时拥有实值(value)和键值(key),set元素的键值就是实值,实值就是键值,set不允许有两个相同的元素。
  • set元素不能改变,在set源码中,set<T>::iterator被定义为底层TB-tree的const_iterator,杜绝写入操作,也就是说,set iterator是一种constant iterators(相对于mutable iterators)
  • 底层机制 红黑树

map

  • map的所有元素都会根据元素的键值自动排序。
  • map的所有元素都是pair,同时拥有实值(value)和键值(key)。pair的第一元素为键值,第二元素为实值。map不允许有两个相同的键值。
  • 如果通过map的迭代器改变元素的键值,这样是不行的,因为map元素的键值关系到map元素的排列规则。任意改变map元素键值都会破坏map组织。如果修改元素的实值,这是可以的,因为map元素的实值不影响map元素的排列规则。因此,map iterator既不是一种constant iterators,也不是一种mutable iterators。
  • 底层机制 红黑树

multiset

  • multiset 的特性以及用法和 set 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique()。

multimap

  • multimap 的特性以及用法和 map 完全相同,唯一的差别在于它允许键值重复,因此它的插入操作采用的是底层机制 RB-tree 的 insert_equal() 而非 insert_unique()。

hash table 散列表

二叉搜索树具有对数平均时间表现,但这样的表现构造在一个假设上:输入数据有足够的随机性。hashtable这种结构在插入、删除、查找具有“常数平均时间”,而且这种表现是以统计为基础,不需依赖元素的随机性。

hashtable底层数据结构为分离连接法的hash表,如下所示:

hashtable中的buckets使用的是vector数据结构,当插入一个元素时,找到该插入哪个buckets的插槽,然后遍历该插槽指向的链表,如果有相同的元素,就返回;否则的话就将该元素插入到该链表的头部。(当然,如果是multi版本的话,是可以插入重复元素的,此时插入过程为:当插入一个元素时,找到该插入哪个buckets的插槽,然后遍历该插槽指向的链表,如果有相同的元素,就将新节点插入到该相同元素的后面;如果没有相同的元素,产生新节点,插入到链表头部)

hash_set

  • 以 hash table 为底层机制
  • 运用set,为的是快速搜寻元素。这一点,不论其底层是RB-tree或是hashtable,都可以完成任务,但是,RB-tree有自动排序功能而hashtable没有,即set的元素有自动排序功能而hash_set没有。

hash_map

  • hash_map 以 hashtable 为底层结构,由于 hash_map 所提供的操作接口,hashtable 都提供了,所以几乎所有的 hash_map 操作行为都是转调用 hashtable 的操作行为结果。RB-tree 有自动排序功能而 hashtable 没有,反映出来的结果就是,map的元素有自动排序功能而 hash_map 没有。

hash_multiset

  • hash_multiset 的特性与 multiset 完全相同,唯一的差别在于它的底层机制是 hashtable,因此,hash_multiset 的元素是不会自动排序的。

hash_multimap

  • hash_multimap 的特性与 multimap 完全相同,唯一的差别在于它的底层机制是hashtable,因此,hash_multimap 的元素是不会自动排序的。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 218,204评论 6 506
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 93,091评论 3 395
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 164,548评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,657评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,689评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,554评论 1 305
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,302评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,216评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,661评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,851评论 3 336
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,977评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,697评论 5 347
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,306评论 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,898评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 33,019评论 1 270
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 48,138评论 3 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,927评论 2 355