STL深入探究(二、容器的底层实现)

1 前言

上一篇 STL深入探究(一、空间配置器)我详细总结了SGI STL采用的空间配置机制,这一篇来总结一下stl容器的底层实现机制。

2 序列式容器


2.1 Vector


Vector实现方式类似于“数组”,与array的数据安排和操作方式非常类似,两者唯一的差别就在于空间运用的灵活性。
array是静态空间,一旦配置了就不能改变,要想换一个大点的空间,就必须由客户自己实现:(1)配置一块新空间;(2)将元素从旧址一一搬到新址;(3)释放原空间。
而vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。

2.1.1 vector的数据结构

vector的数据结构非常简单:线性连续空间,以两个迭代器start、finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间的尾部。
为了降低空间配置的速度成本,vector的实际配置大小可能会比客户端需求量更大,已备将来可能扩充。

2.1.2 vector 构造与内存管理

vector缺省使用std::alloc作为空间配置器,并另外定义了一个data_allocator为了更方便以元素大小为配置单位。
当以push_back()函数将新元素插入vector尾端,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish,使得vector变大;如果没有备用空间,就扩充空间(重新配置(需求的两倍)、移动数据、释放原空间)。

对于vector支持的动态增加大小,并不是在原空间之后接续新空间(因为并不能保证原空间之后尚有足够的可供配置的新空间),而是以原大小的两倍另外配置一块较大空间,然后将原空间内容拷贝过来,再开始在原内容之后构造新元素,并释放原空间。

所以说,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就都失效。

2.2 list


相比较于vector的连续线性空间,list就比较复杂,每次插入或者删除一个元素都会独立的配置或释放一个元素空间。list对于空间利用非常精准,不会浪费。
list是环状双向链表,我们在数据结构中都曾学习过它的特性与使用方法,不再多说。值的一提的是,list由于其双向性,必须提供前移与后移的能力,所以提供BidirectionalIterators,而vector提供的是Random Access Iterators。

注:
list有一个重要性质,插入(insert)和接合(splice)操作都不会造成原有的list迭代器失效。list的元素删除(erase)操作仅仅“指向被删除元素”的迭代器失效,其它迭代器不受影响。 vector并做不到这一点,因为vector的插入操作可能引起空间重新配置,这导致原有的迭代器全部失效。


另外,SGI list不仅仅是一个双向链表,而且是一个环状双向链表,所以它只需一个指针即可完整表示整个结构。


2.3 deque


vector底层采用的是一个数组来实现,list底层采用的是一个环形的双向链表实现,而deque则采用的是两者相结合,所谓结合,并不是两种数据结构的结合,而是某些性能上的结合。我们知道, vector支持随机访问,而list支持常量时间的删除,deque支持的是随机访问以及首尾元素的插入删除 。
deque与vector的最大差异,一在于deque允许常数时间内对起头端进行元素的插入和删除,二在于deque没有所谓的容量概念,因为它是以分段连续空间组合而成,随时可以增加一段新的空间并连接起来。换句话说,像vector那样因旧空间不足而重新配置更大空间,复制元素,释放原空间的事情,deque是不会出现的。也因此,deque不需要提供所谓的空间保留。


2.3.1 deque的数据结构


如上图所示,deque是由一段一段的定量连续空间构造。一旦有必要在deque的前端或尾端增加新空间,便配置一段连续空间,串接在整个deque的头部或尾部。
deque采用一块所谓的map(不是STL的map容器)作为主控,这里的map也是一块连续空间,其中每个元素为一个节点(也是deque的迭代器),指向另一段较大的连续线性空间,称为缓冲区,缓冲区才是deque的储存空间主体。

2.3.2 deque的迭代器


下面,来看一下deque的迭代器设计。

举例来说,如果deque中存储20个元素,每个缓冲区大小为8,则需要20/8=3个缓冲区,所以map中会运用3个节点。deque的begin()和end()始终会返回map中节点的头和尾,名为start和finish,其中,start的cur指向第一个缓冲区中的首元素,finish的cur指向最后一个缓冲区的最后一个元素的下一位置。

2.3.3 deque的构造和内存管理

deque的缓冲区扩充操作比较繁杂,此处仅仅截取《STL源码剖析》的几张图展示,具体细节见书籍P153-166。





注:上面的连环图解,充分展示了deque容器的空间处理。那么现在就出现一个问题,什么时候map需要重新整治?
     如果无穷尽的向deque中存储数据,map所含有的缓冲区肯定会填满的。也就是说,当map的前端节点或后端节点备用空间不足时,map便也需要重新配置,依然是经过三个步骤,配置更大的、拷贝原来的、释放原空间。





  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值