浅谈STL---vector
1. STL中的内存管理:
首先,先了解一下STL中是如何进行内存的分配和管理的。STL内存管理机制,是将空间配置和构造内容分开,空间分配和释放用alloc::allocate()和alloc::deallocate(),对象构造和析构用alloc::construct()和alloc::destroy()。在空间配置上,STL采用双层级配置器,第一级用malloc()和free()配置和释放内存,第二级采用的策略是:当需要的内存超过128字节时,用第一级的分配方法;当需要的内存小于128字节时,采用二级配置器分配,具体如下:
将一块内存块分为大小间距为8字节的16种内存块,大小从8字节-128字节,然后用数组+链表方式管理(free_list[16],每个free_list维护一个链表,链表里存放着大小相同的内存块)。每个数组(free_list)中存储的是大小相同的用链表连接起来的内存块。申请内存的请求到达后,先将请求的内存块大小按8的倍数对齐,然后检查所申请的内存块对应的数组中链表是否为空,若不为空则直接返回第一个节点给程序。二级配置器还维护一个内存池,若对应的数组中链表为空则像内存池申请,调用refill 函数从内存池取出空间(取空间要调用chunk_alloc函数,默认取出20个对应需求块大小)挂在该大小的数组free_list的链表下,再分配给所需程序。若内存池为空则用malloc申请。若malloc不够的话则发出异常。
2. vector的定义:
vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素,它的空间配置器默认是alloc,迭代器类型是 typedef T* iterator,也就是类型T的普通指针。此外,vector维护的是一个线性的空间,所以不论元素类型是什么,普通指针都可以作为它的迭代器而满足所有操作。vector也支持随机存取。
3. vector的数据结构:
vector是线性空间,它有三个迭代器指向它的连续空间,分别是:start指向空间中已被使用的头,finish指向已被使用的尾,end_of_storage指向可用空间的尾。实际上,vector分配的空间总是要比需求的空间大一些,也就是说vector的容量永远大于或等于其大小(capacity >= size)。
在增添新元素时,如果超过当时的容量,则容量会扩充,在linux下的gcc中扩充2倍,windows下的vs中扩充1.5倍。扩充要经历“重新分配空间”、“元素复制移动”、“释放原空间”的三大操作。
4. vector的构造和内存管理:
vector的空间配置器用的是alloc,它的构造函数主要用的库函数是:allocate和uninitialized_fill_n,前者负责分配内存块,后者负责在内存块上构造内容,也就是STL常用的分配内存和构造内容分离的策略。
当用push_back添加元素时,会先检查是否有备用空间,如果有就在备用空间上调用construct函数构造元素,并调整迭代器finish使vector的size变大。
如果没有备用空间了,它内部会调用一个insert_aux的成员函数,这个函数的主要工作是:
(1)调用allocate函数分配一块比原来大2倍的内存(gcc方式)
(2)调用uninitialized_copy函数将原空间的旧内容复制到新空间
(3)调用construct函数在新空间上构造新内容。
(4)调用destroy函数析构原空间的内容。
(5)调用deallocate函数释放原空间。
(6)调整start、finish、end_of_storage迭代器的位置。
在上述过程中一旦发生异常,则回滚事件:对新空间调用析构和释放函数。
(ps:insert_aux在insert函数时也会被调用,此时会有插入点position,所以(2)(3)要改为:(2)复制旧空间position之前的内容,(3)构造新内容,(4)复制旧空间position之后的内容)
5. vector的元素操作:
vector的pop_back成员函数:调用destroy函数析构finish处内容,再将finish减1。
vector的erase成员函数:先调用copy函数将要删除区间之后的数据前移,再调用destroy函数析构finish处的内容,最后调整finish,将其减小。
vector的clear成员函数:直接调用erase析构start到end之间所有内容,但是capacity依旧是不变的。

浙公网安备 33010602011771号