STL源码剖析—顺序容器

一、vector

1、vector简介:

  vector的数据安排及其操作方式与数组非常相似,微小的差别在于空间的使用,数组是静态空间,一旦配置了就不能改变。vector是动态空间,随着元素的加入,它的内部机制会自行扩充空间以容纳新元素。

2、vector的构造和内存管理

其数据结构为:

复制代码
1 class vector
2 {
3     ...
4 private:
5     iterator start;        //表示目前使用空间的头
6     iterator finish;        //表示目前使用空间的尾
7     iterator end_of_storage; //表示目前可用空间的尾
8     ...
9 };
复制代码

vector提供许多的constructs,其中一个允许我们指定空间大小及初值。

复制代码
 1 vector(size_type n, const T& value)   //构造函数,允许指定vector大小n和初值value
 2 {
 3     fill_initialize(n, value);
 4 }
 5 void fill_initialize(size_type n, const T& value)
 6 {
 7     start = allocate_and_fill(n, value);
 8     finish = start + n;
 9     end_of_storage = finish;
10 }
11 iterator allocate_and_fill(size_type n, const T& x)
12 {
13     iterator reslut = data_allocator::allocator(n);//配置n个元素空间
14     uninitialized_fill_n(result, n, x); //全局函数
15     return result;
16 }//uninitialized_fill_n()根据第一参数的型别特性决定使用fill_n()或反复调用construct()来完成任务,
//如果[first,n)范围内的每一个迭代器都指向未初始化的内存,那么uninitialized_fill_n()会调用copy construct,在该范围内的产生x的复制品,
//也就是在该范围内的每个迭代器都会调用construct(&*i,x),在对应位置上产生x的复制品.
复制代码

当我们以push_back()将新元素插入vector尾端时,该函数首先检查是否还有备用空间,如果有就直接在备用空间上构造元素,并调整迭代器finish,使vector变大,如果没有备用空间了,就扩充空间,扩充空间的过程包括重新配置、移动数据和释放原空间。

注:当没有备用空间时,为了时间成本,容量会扩充至两倍,如果两倍容量仍不足,就扩充至足够大的容量。另外,对vector的任何操作,一旦引起空间重新配置,指向原vector的所有迭代器就失效了。

复制代码
 1 void push_back(const T& x)
 2 {
 3     if (finish != end_of_storage)  //还有备用空间
 4     {
 5         construct(finish, x);    //构造元素
 6         ++finish;                
 7     }
 8     else
 9     {
10         insert_aux(end(), x);      //如果没有备用空间,调用该全局函数
11     }
12 }
13 insert_aux(iterator position, const T& x)   
14 {
15     if (finish != end_of_storage)   //还有备用空间,
16     {        
17         construct(finish, *(finish - 1));    //在备用空间起始处构造一个元素,并以vector最后一个元素值为其初始值
18         ++finish;
19         T x_copy = x;
20         copy_backward(position, finish - 2, finish - 1);
21         *position = x_copy;
22     }
23     else                            //没有备用空间
24     {
25         const size_type old_size = size();
26         const size_type len = old_size != 0 ? 2 * old_size : 1;
27         //配置原则:如果原大小为0,则配置一个元素大小空间
28         //如果原大小不为0,则配置为原大小的两倍,
29         //前半段用来放置原数据,后半段准备用来放新数据
30         iterator new_start = data_allocator::allocator(len); //配置大小为len的元素空间
31         iterator new_finish = new_start;
32         try
33         {
34             new_finish = uninitialized_copy(start, position, new_start);  //将原vector的内容拷贝到新vector中
35             construct(new_finish, x);    //为新元素设定初值x;
36             ++finish;
37             new_finish = uninitialized_copy(position, finish, new_finish);//将安插点的原内容拷贝过来
38         }
39         catch (...)
40         {
41             destory(new_start, new_finish);
42             data_allocator::deallocate(new_start, len);
43             throw;
44         }
45         //析构并释放原vector
46         destroy(begin(), end());
47         deallocate();
48         //调整迭代器,指向新的vector
49         start = new_start;
50         finish = new_finish;
51         end_of_storage = new_start + len;
52     }
53 }
复制代码

3、vector的元素操作

vector所提供的元素很多,见下图

这里只选择几种典型的操作进行介绍。详细参考http://www.cplusplus.com/reference/vector/vector/?kw=vector。

复制代码
 1 iterator erase(iterator first, iterator last)            //清除[first,last)中所有的元素
 2 {
 3     iterator i = copy(last, finish, first);    
 4     destroy(i, finish);
 5     finish = finish - (last - first);
 6     return first;
 7 }
 8 
 9 iterator erase(iterator position)  //清除某个位置上的元素
10 {
11     if (position + 1 != end())
12     {
13         copy(position + 1, finish, position);  //该函数将[position + 1,finish)的元素拷贝到从position开始的位置上
14     }
15     --finish;
16     destroy(finish);
17     return position;
18 }
复制代码

 

二、list

1、list简介

相对vector的连续性空间,list则为离散的空间布局,每次插入或删除一个元素,就会配置或释放一个元素空间。

2、list的构造和内存管理

SGI list是一个环状双向链表,其节点结构为:

复制代码
1 template <class T>
2 struct _list_node
3 {
4     typedef void* void_pointer;
5     void_pointer prve;
6     void_pointer next;
7     T data;
8 };
复制代码

以下四个函数,分别用来配置、释放、构造、销毁一个节点:

复制代码
 1 link_type get_node()  //配置一个节点
 2 {
 3     return list_node_allocator::allocator();
 4 }
 5 void put_node(link_type p) //释放一个节点
 6 {
 7     list_node_allocator::deallocate(p);
 8 }
 9 link_type create_node(const T& x) //产生一个节点,带有元素值
10 {
11     link_type p = get_node();
12     construct(&p->data, x);
13     return p;
14 }
15 void destroy_node(link_type p)  //销毁(析构释放)一个节点
16 {
17     destroy(&p->data);
18     put_node(p);
19 }
复制代码
1 void empty_initialize()  //产生一个空链表
2 {
3     node = get_node();
4     node->next = node;   //令头尾都指向自己
5     node->prve = node;
6 }

3、list的元素操作

主要的操作包括:

 

复制代码
 1 iterator insert(iterator position, const T& x)  //在迭代器position所指位置出插入一个节点,内容为x
 2 {
 3     link_type tmp = create_node(x);
 4     tmp->next = position.node;
 5     tmp->prve = position.node->prve;
 6     (link_type(position.node->prve))->next = tmp;
 7     position.node->prve = tmp;
 8     return tmp;
 9 }
10 
11 iterator erase(iterator position)  //移除迭代器position所指节点
12 {
13     link_type next_node = link_type(position.node->next);
14     link_type prve_node = link_type(position.node->prve);
15     prve_node->next = next_node;
16     next_node->prve = prve_node;
17     destroy_node(position.node);
18     return iterator(next_node);
19 }
20 
21 void remove(const T& value)//将数值为value的元素移除
22 {
23     iterator first = begin();
24     iterator last = end();
25     while (first != last)
26     {
27         iterator next = last;
28         ++next;
29         if (*first == value)
30             erase(first);
31         first = next;
32     }
33 }
复制代码

list还提供一个迁移操作,将某连续范围的元素迁移到某个特定位置之前,这个操作为其他人如splice、sort、merge奠定了基础。

复制代码
 1 void transfer(iterator position, iterator first, iterator last)  //将[first,last)中的元素移到position之前
 2 {
 3     if (position != last)
 4     {
 5         (*(link_type((*last.node).prve))).next = position.node;
 6         (*(link_type((*first.node).prve))).next = position.node;
 7         (*(link_type((*position.node).prve))).next = first.node;
 8         link_type tmp = link_type((*position.node).prve);
 9         (*position.ndoe).prve = (*last.node).prve;
10         (*last.node).prve = (*first.node).prve;
11         (*last.node).prve = tmp;
12     }
13 }
复制代码
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值