《STL源码剖析》学习--六大组件

来源:互联网 发布:怎样下载excel2003软件 编辑:程序博客网 时间:2024/06/05 01:58

stl 提供了六大组件,分别为:容器、算法、迭代器、仿函数、适配器和配置器。

容器通过配置器取得数据存储空间,算法通过迭代器存取容器的内容,仿函数可以协助算法完成不同的策略,配接器可以修饰或者嵌套仿函数。

下面分别简单介绍:

1.容器(container) --  各种数据结构

根据数据排列方式分为序列式和关联式:

1.1序列式容器

(1)vector:与C++提供的静态空间Array相比,vector是动态空间。连续空间,支持随机存取,迭代器是普通指针。动态增加大小,不是在原空间后面续接新空间,而是以原来两倍的大小另外开辟空间,然后将原来内容拷贝过来,再在之后添加新元素,释放空间,一个“配置新空间,数据移动,释放旧空间”的过程,因此插入元素不一定是常数时间。当配置新空间后,原来迭代器失效。vector不适用于插入删除频繁或者数据量很大的运算,会不停的开辟新空间,释放空间。

单项开口,只能一端插入删除元素。

(2)list:双向链表,不支持随机存取,非连续空间,对于任何位置插入或者移除元素都是常数时间。当插入或者删除元素时,除此元素外迭代器不失效。

双向开口,前后端都可以插入删除元素。

提供transfer迁移操作,可以将连续范围的元素迁移到某个特定位置之前,这是节点之间的移动;

提供splice接合操作,将某连续范围的元素从一个list移动到另一个list的某个定点;

还有merge、reverse和sort操作。

(3)deque :连续线性空间,随机存取,双向开口,双端插入删除,允许常数时间内对头端进行元素的插入删除。动态地以分段连续空间组合而成,因而没有预留空间,随时可以增加一段新空间并连接起来。deque的随机存取的代价很大:具有map作为主控,map是一小块连续空间,其中每个元素指向另一端较大的连续空间(缓冲区);具有复杂的迭代器架构,维护两个迭代器,start和finish,分别指向缓冲区的第一个元素和最后缓冲区的最后一个元素。当插入删除元素时还包括map的分配更新,缓冲区的分配,迭代器的更新。

(4)stack :一种配接器,底层是deque,通过改变接口,来实现“后进先出”,不允许遍历,没有迭代器。

(5)queue :跟stack一样是一种配接器,底层是deque,先进先出,不允许遍历,没有迭代器。

(6)heap:不属于stl,有大堆和小堆之分,根据完全二叉树堆排序算法来保持一个大堆或者小堆,从而可以每次取得优先权最高的的元素。heap所有元素都必须遵循特别的排序规则,不提供遍历功能,没有迭代器。

(7)priority_queue :也是一种配接器,底层是vector,再加上heap排序规则,修改接口,实现自动排序,权值高的在前,只能取走顶端元素,没有迭代器。

(8)slist:和list类似,只是是单向链表,只有forward iterator,处于效率的考虑,针对此提供了insert_after()和erase_after()。只提供pop_front(),元素次序和插入次序相反。

1.2 关联式容器:

(1)RB-tree:即红黑树,stl不公开,红黑树是一种平衡二叉树,置于红黑树的计算过程不做介绍,可参考《算法导论》13章红黑树。这种结构对于数据搜索效率很高,时间复杂度O(lgN)。

(2)set :根据元素键值(也是实值)自动排序,不允许键值重复,底层是红黑树。

(3)map :根据元素键值(第一元素)自动排序,不允许键值重复,底层是红黑树。

(4)multiset: 与set相同,只是允许键值重复。

(5)hashtable: 使用映射的方式将元素映射为大小可接受的索引。解决碰撞简单的有线性探测,但是容易造成主集团;引出二次探测,可能引发次集团;还有一种方法开链,在每个表格元素中维护一个list,sgi stl中采用这种方法。

(6)hash_set:类似于set ,底层机制为hashtable,因为hashtable没有排序,hash_set 没有自动排序。

(7)hash_map:类似于map,只是底层机制为hashtable,同样没有自动排序。

(8)hash_multiset:类似于multiset,允许键值重复,没有排序。

(9)hash_multimap:类似于multimap,底层机制为hashtable,允许键值重复,没有排序。

2.算法(alogrithm)-- 各种算法

这里不做具体介绍,都是高效的算法,需要自己研究。

3.迭代器(iterator)-- 容器和算法之间的胶合剂,所有容器都有自己专属的迭代器。

(1)注意迭代器实现中traits编程手法的实现。详细可见traits

(2)迭代器有五种型别:Input、Output、Forward、Bidirectional和Random Iterator。

4.仿函数(functor)-- 类似函数的东西,作为算法的一种策略。

具体见可能令你困惑的C++语法中最后的仿函数。

5.配接器 (adapter) -- 用来修饰容器、仿函数或者迭代器接口的东西,使原本不能在一起工作的能工作在一起。

在《设计模式》一书中有此设计模式,定义为:将一个class的接口转换为另一个class的接口,使原本因接口不兼容而不能合作的classes,可以一起运行。

stl中的配接器有:container、iterator和functor。containeradapters 有stack和queue,底层都是deque;iterator adapters有insert 、reverse 和stream;function adapters有对返回值进行否定的not1和not2、对参数进行绑定的bind1st和bind2nd、用于函数合成的compose1和compose2、用于函数指针ptr_fun、用于成员函数指针mem_fun和mem_fun_ref。

6.配置器 (allocator) -- 负责空间的配置和管理

(1)容器缺省的配置器为alloc,而非allocator。

(2)stl 将内存空间的配置/释放和对象内容的构造/析构分开,由不同操作负责。内存配置操作由alloc:;alllocate()负责,内存释放由alloc::deallocate()负责;对象构造由::construct()负责,对象析构由::destroy()负责。

destroy()有两个版本,第一个版本直接调用对象的析构函数;第二个版本,如果对象的析构是无关痛痒的(其__type_traits<T>是true),则什么也不做,否则,调用第一个版本。

(3)内存空间采用双层级配置器。第一级使用malloc()和free(),第二级则视不同的情况采取不同的策略,当配置区域块超过128bytes时,调用第一级配置器;当配置区域小于128byte时,采用memory pool整理方式。因为在小额区太多时,配置时负担也重,所以在小区域时用的策略也多。

(4)二级配置器,主动将小额区块的内存需求上调至8的倍数,并维护16个freelists,各自管理大小分别为8、16、24、32…128bytes的小额区块。

 

0 0