首页 > 代码库 > STL源码剖析—序列容器
STL源码剖析—序列容器
STL源码剖析—序列容器
对于STL中的容器,存在一定的内含关系,例如,heap内含一个vector,priority-queue内含一个hep,stack和queue都含有一个deque,set/map/multiset/multimap都内含一个RB-tree,hash_x都内含一个hashtable。
对于序列容器来说,vector和list的插入都是在指向迭代器之前进行插入。同时需要注意的就是,对于vector来说,调用erase()函数也就是清除了几个连续空间上的元素,调用其析构函数,并没用释放原来元素占用的内存,即使clear函数也没有释放内存,而只是清除了原来vector内的所有元素。等待vector析构的时候才会将内存释放,但是这一点和list还是有区别的,对于List来说,erase也就是删除一个节点,调用clear是删除了List中的所有节点也就让List内存得到了释放。这是一点不同,last的地方。
同时对于List来说,有一个特殊的地方就是具有transfer函数,这个函数可以迁移,将一段区间中的节点迁移到某一个节点的前面。同时transfer所接收的[first,last)区间也可以是同一个list.但是transfer并非是公开接口,list公开提供的是所谓的接合操作splice。
同时list容器内部还提供了reverse()sort() merge()函数,其实这些函数在算法库中也有提供,但是在Effective STL中也提到了,如果容器内部自定义了函数,那么最好使用容器内部的成员函数。
Deque是一种双向开口的连续线性空间,可以在头尾分别做元素的插入和删除操作。Deque允许在常数时间内对起头端进行元素的插入或移除操作。Deque没有容量的概念,因为他是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。像vector那样因为空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间这样的事情在deque中是不会发生的。Deque也提供random access iterator,但是他的迭代器不是普通指针,对deque进行排序操作,为了提高效率,可将deque先完整复制到一个vector身上,将vector排序后,在复制回deque.
要明白vector扩充容量的代价是很大的,需要申请更大的空间,倒腾数据,销毁原始数据。Deque由一段一段的定量连续空间构成,一旦有必要在deque的前端或尾端增加新空间,便配置一段定量连续空间,串联在整个deque的头部或尾部。要明白deque对外提供的是和vector一样的迭代器类型,所以在内部是很麻烦的。Deque内部是一个指针数组,元素是一段联系的内存。其实deque对外的封装所有的操作都是和vector类似的,只不过内部操作很复杂。Deque中的clear函数也是只有一个缓冲区,所有的数据全部被销毁,回到了deque的最初状态。
Stack并不被成为容器,更像是配接器,stack所有元素的进出都必须符合“先进先出”的条件,只有stack顶端的元素,才有机会呗外界取用。默认情况下stack底层使用deque来实现的。Stack提供底层实现机制的入口,默认是使用deque
Queue是先进先出的数据结构。默认情况下queue是使用deque最为底层结构的。其实使用list也可以作为queue和stack的底层结构。Queue和stack都不算作容器而是配接器类型。底层默认都是使用deuque
Heap不是STL组件,但是是priorityqueue的助手。
如果使用二叉查找树最为优先级队列的底层机制,元素的插入和删除就是Log的级别,显得小题大做,因为二叉查找树的输入需要随机性,并且不容易实现,最后优先级队列的底层实现选择完全二叉树,也就是二叉堆。同时完全二叉树的存储右可以使用数组。这么一来,一个数组和一组heap算法来插入元素、删除元素,取极值,将一数组排列成一个heap,最后也即是使用了最大堆或者最小堆来实现了优先级队列,里面使用了heap的算法操作。存储就使用vector。关于heap的算法有push_heap pop_heap sort_heap make_heap 只有这四个函数。
但是这四个函数在构造优先级队列时非常重要。优先级队列底层默认是使用vector存储元素,但是使用的是heap系类的函数来处理存储的数据的。对外封装的函数有
构造函数priority_queue empty() push() pop() top() 默认使用的最大堆。
STL中的默认链表是个双向链表list,同时还提供了一个单向链表slist.slist和list的主要差别是前者的迭代器是单向的forward iteratro后者是双向的bidirectional iterator 他们的插入移除接合等操作并不会造成原有的迭代器失效。有一点提醒一下,对于插入vector和list都是将元素插入到指定迭代器前面。但是对于slist来说有点困难。必须从头找,所以除了slist起点外附近的区域之外,其他位置上采用insert或erase操作函数都不合适。Slist提供了Insert_after()和erase_after()。
但是要注意,这里实现的slist,和内核实现的思想是一样的。忘记说一点,STL中的list是带有头结点的双向链表,对于链式存储(包括deque)在调用clear函数后就销毁了所有的内存,对于list还实现了swap函数。还提供了push_front() pop_front().
Slist也是有头结点的begin()返回的是head的next指针。
总结:对于序列容器,STL中实现了几种标准的容器,同时还实现了几个配接器,容器有vector list deque slist(非标准) 配接器有stack queue priority_queue
Vector就是连续的内存空间list就是双向链表,deque比较复杂,但是stack 和queue默认都是deque来实现的。Priority_queue存储元素虽说使用的是vector,但是相应的操作都是和heap相关的。
对于函数来说,比较特殊的就是list,因为他的特殊性,他内部实现了很多本来算法库就有的函数,比如sort merge remove。注意deque list slist的clear函数不同寻常
STL源码剖析—序列容器