STL源码剖析—整体简要概述

STL源码剖析—简要概述

首先需要明白STL内部各个组件以及组件之间的关系,STL号称是泛型编程的典范,泛型编程不仅仅是模板的高级应用,这里更多体现了整体的设计思想。

空间配置器、迭代器、容器、算法、仿函数、适配器,共六大组件,其中这里比较关心的就是前四个。这里面涉及了模板、模板的偏特化、模板的特化。所有的操作都是为了更高的效率而存在的。

一、空间配置器

对于一个STL的模板库来说,空间的分配极其重要,因为所有元素的存储都需要空间来分配。STL的空间配置器是自己定义的,使用了二级配置的想法,对于申请空间大于128KB的情况使用以及配置,也就是直接使用malloc申请空间,对于申请空间小于128KB的情况使用二级配置,也就是内存池的管理,每次配置一大块内存,并维护对应之自由链表,下次若再有相同大小的内存需求,就直接从链表中取出,可以将这里的内存池看成使用开链法的hashtable,某一个桶内存储的都是大小相同的小内存块。

对于常规的内存操作new包含两个阶段的操作,调用operator new配置内存,调用对象的构造函数构造对象内容,delete算式也包含两个阶段,调用对象的析构函数,调用operatordelete释放内存。为了精细分工,STLallocator决定将这两个阶段的操作区分开来,内存配置操作由alloc::allocate()负责,内存释放操作由alloc::deallocate()负责,对象构造操作由::constructor()负责,对象析构操作由::destroy()负责。所以从这里也可以看出来,对象析构并不代表内存已经释放,在有些容器(比如vector)就是这样的过程,调用clear知识容器内的对象调用了对应的析构函数,并不代表这块内存已经被释放掉.

对于destroy()函数也是用了特化的处理,对于有些没有必要调用析构函数的对象就不用调用析构函数,当然为了判断析构函数是否无关紧要,就用到了萃取的功能,value_type()获得迭代器所指对象的型别,再萃取特性判断析构函数的性质。内存的申请最终封装成一个类(simple_alloc,在以后的容器中经常会看到对这个类的typedef),类里面的函数属性均为static。

 

为了配合使用,在STL内部还定义了几个全局函数用来填充或复制大块内存数据,

Uninitialized_copy(),uninitialized_fill()、uninitialized_fill_n()函数

总结:大块内存的申请使用allocate和deallocate(),内部使用的malloc和free,小块内存是从内存池中取。对象的构造和析构使用contructor和destroy(),内部使用placement new和调用对应的析构函数。为了内存更加方便的使用,这里由添加了几个全局的函数,三个全局的函数对应算法库中的copu,fill.fill_n。为了更高效的完成工作,对某些函数使用了模板的特化和泛化。

二、迭代器

迭代器是粘合算法和容器的枢纽,正是有了迭代器是的算法和容器相互对立,同时有可以有关联,对于每一种容器,内部都是相应的迭代器的声明,这些声明正是算法中需要使用的内容,这样就达到了容器和算法结合的特性。同时迭代器分为好几种,比如vector的迭代器就是随机迭代器,hashtable的迭代器就是前向迭代器,红黑树的迭代器是双向迭代器。迭代器的几种类别使得算法中更加好用。同时STL中的萃取使得很多工作变得很简单。

三、容器

容器分为序列容器和关联容器。Vector是序列容器的典范,对于申请的内存总是不必须要的内存少。对于迭代器失效,内存连续的失效情况最严重,当然遇到内存从新分配,都是一样的效果(关联容器中以红黑树为底层实现的容器是不会出现内存重新分配的情况,但是以hashtable为底层实现的关联容器会出现内存重新分配的情况)。Vector的clear操作并没有释放内存,只是调用了对象的析构函数。如果内存不够使用,就需要重新分配内存(分配的内存时原来内存大小的两倍),然后进行搬移,在vector内部没有push_front的操作。对于vector的inster操作中,如果不重新分配内存,也需要看插入点之后的元素和插入元素的个数之间的比较,根据比较的结果来进行如何插入。容器中所有的插入都是指在某一点之间插入。对于双向链表slist是带有头节点的双向链表,由于是双向链表,便有push_front,pop_front之说。同时删除一个节点就是对这个节点所占的内存空间进行释放,slist的迭代器提供的双向迭代器,对于slist内部有自己实现和算法中的部分函数,例如merge,sort,reverse,clear。内部的transfer函数对是前面几个函数的基础。对于STL中的容器来说,如果容器本身实现了函数,就没有必要使用算法库中的函数。Deque是双端队列,他本身也会可能出现内存重新分配的情况,deque迭代器的前置和后置都是相当复杂的。Deque的设计和开链法的hashtable很相似,中枢管理着内存块,内存块中存储着数据,deque也属于非连续内存空间,所以deque中也有push_front、pop_front的操作。对于deque调用clear,只是删除了存储数据的内存块,并没有将中枢删除。这里的前置和后置需要考虑的就是考虑跳跃内存块的情况。关联式容器以hashtable和rb-tree作为底层实现。

四、算法

对于算法的部分,都是以迭代器作为参数,对于某一个函数的实现可能有不同的重载版本。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值