STL源码之vector容器深入学习

任何特定的数据结构都是为了实现某种特定的算法,STL容器也不例外。在进入vector容器的学习之前,我们先来梳理一下有关容器相关的概念。

(1)常用的数据结构包括哪些?array(数组)、list(链表)、tree(树)、stack(堆栈)、queue(队列)、hash table(散列表)、set(集合)、map(映射表)…
(2)根据数据在容器中的排列特性,这些数据结构分类序列式(sequence)和关联式(associative)两种。
(3)所谓序列式容器,其中的元素是可序的(ordered),但未必有序(sorted)。
(4)C++语言本身提供了一个序列式容器内置数组array,STL另外再提供vector, list, deque, stack, queue, priority-queue等等。
(5)其中stack和queue只是将deque改头换面,技术上被归类为一种配接器(adapter)。

  1. 引言
    我们从序列式容器vector开始进入STL容器的学习。我们知道内置数组array是静态空间,一旦配置了就不能改变。如果日后有需求要对数组内容进行扩充怎么办呢?难道要重新申请一个更大的空间,将其数据一一复制到新空间上吗?那将严重导致效率的低效。那还有其他办法?
    有的,vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间纳入新元素。
  2. SGI STL vector源码
    当我们沉溺于vector方便、高效的内存处理机制的时候,有没有想过它的内部究竟在怎么实现的呢,来看看SGI STL中vector的部分实现细节,一窥究竟:
  3. vetor的迭代器
    (1)vector维护的是一个连续线性空间。
    (2)不论其元素类型为何,普通指针都可以作为vector的迭代器而满足所有必要条件。因为vector迭代器所需要的操作行为,如operator*, operator->, operator++, operator–, operator+, operator-,opreator+=, operator-=,普通指针天生就具备。
    (3)vector支持随机存取,而普通指针正有着这样的能力,所以vector提供的是Random Access Iterator。
    vector中迭代器部分源码:
  4. vector的数据结构
    (1)vector所采用的数据结构非常简单:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围。并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端:

    (2)vector实际配置的大小可能比客户端需求量更大一些,以备将来可能扩充。
    (3)使用start, finish, end_of_storage三个迭代器,便可轻易地提供首尾标示、大小、容量、空容器判断、标注运算子[]、最前端元素值、最后端元素值等…:

    结合下图便于理解:STL源码图4-2
  5. vector的构造与内存管理:constructor, push_back
    (1)我们在配置器部分已经说过,SGI STL配置器默认使用alloc作为空间配置器,并据此另外定义了一个data::allocator,为的是更方便以元素大小为配置单位。
    (2)uninitialized_fill_n()会根据第一参数的类型特性(type traits)决定使用算法fill_n()或反复调用constructor()来完成任务。
    (3)使用push_back()将新元素插入vector尾端时,该函数首先检查是否还有备用空间?
    有—>直接在备用空间上构造元素,并调整迭代器finish。
    无—>扩充空间(重新配置、移动数据、释放原空间)


    (4)所谓动态增加大小,并不是在原空间之后接续新空间(因为无法保证原空间之后尚有可供配置空间)。
    (5)而是以原大小的两倍另外配置一块较大空间,然后将原内容拷贝过来,然后才开始在原内容之后构造新元素并释放原空间。
  6. vector的元素操作:pop_bcak, erase, clear, insert
    vector所提供的元素操作很多,这里主要讲insert():

    其中copy_backward()函数实现的是逆向拷贝。

    参考:STL源码剖析

发表评论

电子邮件地址不会被公开。 必填项已用*标注