STL源码笔记之序列式容器

      所谓序列式容器,其中的元素都可序,但未必有序。 序列式容器包括array(内建)、vector、heap、priority_queue、list、slist、deque、stack(配接器)和queue(配接器)。

      容器中大量应用前面用于构造的construct和用于析构的distroy以及uninitialized_copy()、uninitialized_fill()和uninitialized_fill_n()。这些函数面对的是未初始化的内存,它们将内存的配置和对象的构造分离。在构造对象的时候对其型别采用判断,然后根据型别调用不同的函数以达到最大化效率。


vector

      vector的数据以及操作方式与array非常相似,两者的唯一差别在于空间的运用的灵活性。array是静态空间,一旦配置了就不能改变。而vector是动态空间,随着元素的加入,它的内部机制会自动扩充空间以容纳新元素。vector的实现技术,关键在于其对大小的控制以及重新配置的数据移动效率。一旦vector的旧有空间满载,元素的空间会成倍的增长。

1.vector提供的接口

       vector提供的接口:包括得到vector的属性接口、vector的操作接口以及构造函数:

     (1)构造函数:vector()、vector(size_type n、const T& value)、vector(size_type n);

     (2)属性函数:begin、end、size、capacity、empty、operator[]、front和back

     (3)操作函数:push_back()、pop_back()、erase()、resize()、clear()。

注:vector没有pop_front和push_front操作。

注:pop_back返回的值为void类型,不是删除元素的值。

注:vector(size_type n、const T& value)这种同时初始化n个元素且初始值为T,只适用于顺序容器。

2.vector的数据结构

       vector采用的数据结构非常简单:线性连续空间,它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间的尾端。vector采用的空间配置器为alloc。


template<class T,class Alloc = alloc>
class vector{
public:
    typedef T value_type;
    typedef value_type* pointer;
    typedef value_type* iterator;//迭代器类型就是元素的指针
    typedef value_type& reference;
    typedef size_t size_type;
    typedef ptrdiff_t difference_type;
    ....
};

注:容量的扩张必须经历:“重新配置、元素移动、释放原空间“等过程。


list

     相对于vector的连续空间,list就显得复杂许多,它的好处是每次插入或删除一个元素,就配置或释放一个元素。因此对于空间的运用绝对精确,一点也不浪费。而且对于任何位置的元素插入或删除,list永远是常数。

1.list提供的接口

     list提供的接口:包括得到list的属性接口、list的操作接口以及构造函数:

   (1)构造函数:list()、list(size_type n、const T& value)、list(size_type n)。

   (2)属性函数:begin、end、empty、size、front和back。

   (3)操作函数:push_back()、pop_back()、push_front、pop_front、erase()、resize()、clear()、unique、splice、merge、reverse、sort、insert。

//pop_front和front函数常常连用
while(!list.empty()){
    process(list.front());
    list.pop_front();
}

注:push_front、pop_front,只有list和deque才有此接口


2.list的数据结构

     list本身和list的节点是不同的,下面是list节点:


template<class T>
struct __list_node{
    typedef void* void_pointer;
    void_pointer prev;
    void_pointer next;
    T data;
};
       list的迭代器设计:

template<class T,class Ref,class Ptr>
struct __list_iterator{
    typedef __list_iterato<T,T&,T*>  iterator;
    typedef __list_iterato<T,Ref,Ptr>  self;
    typedef bidirectional_iterator_tag iterator_category;//双向迭代器
    tyepdef  __list_node<T>* link_type;
    link_type node;//包含了一个指向__list_node节点
    .....
};
     list不仅是一个双链表,还是一个环形的双链表,所以它只需要一个指针,便可以完整表现整个链表。这边主要考虑的问题是双链表的一些操作,包括删除、插入等操作。具体结构如下:


template<class T,class Alloc = alloc>
class list{
protected:
    typedef __list_node<T> list_node;
public:
    typedef list_node* link_type;
    typedef __list_iterator<T,T&,T*> iterator;//包含的迭代器
protected:
    link_type node;//包含了一个指向__list_node节点
    .....
};


Deque

       vector是单向开口的连续线性空间,deque是一种双向开口的连续线性空间。所谓双向开口,就是它的头尾两端都可以插入元素。deque和vector最大的差异在于deque允许在常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓容量(capacity)观念,因为它是动态地分段连续空间组合而成,随时可以增加一段新的空间并连接起来。不会像vector那样”因旧空间不足而重新配置一块更大的空间,然后复制元素,再释放旧空间“这样的事情在deque是不会发生的。虽然deque也提供Random Access iterator,但它的迭代器并不是普通指针。除非特别需要尽可能选择vector,而不是deque。

1.deque提供的接口

     deque提供的接口:包括得到deque的属性接口、deque的操作接口以及构造函数:

   (1)构造函数:deque()、deque(size_type n、const T& value)、deque(size_type n)。

   (2)属性函数:begin、end、size、empty、maxsize()、operator[]、front和back。

   (3)操作函数:push_back()、pop_back()、push_front、pop_front、erase()、resize()、clear()、insert。


2.deque的数据结构

      deque是一个分段连续的空间,维持了整体连续的假象,deque通过一个管控中心,来实现分段连续的结构:

     

deque中iterator的设计:

template<class T,class Ref,class Ptr,size_t BufSize>
struct __deque_iterator{
    typedef __deque_iterator<T,T&,T*,BufSiz> iterator 
    typedef __deque_iterator self;
    typdef T** map_pointer;
    T *cur;
    T *firsr;
    T *lase;
    map_pointer node;//指向管控中心
    ...
}


deque结构的设计(参考上图), 主要考虑问题是buffer边缘问题,超过上边缘或者下边缘的时候需要去map管控中心跳到下一个节点或者上一个节点。具体如下:

template<class T,class Alloc = alloc,size_t BufSize=0>
class deque{
public:
   typedef T value_type;
   typedef T* pointer;
public:
   typedef __deque_iterator<T,T&,T*,BufSiz> iterator;
protected:
   typedef pointer* map_pointer;
   iterator start;
   iterator finish;
   map_pointer map; 
   size_type map_size;
}

stack

    stack是一种先进后出(FILO)的数据结构。它只有一个出口,形式如图所示。stack允许新增元素、移除元素、取得最顶元素。但除了最顶元素外,没有其他任何方法可以存取stack的其他元素。换言之,stack不允许有任何遍历行为。(stack没有迭代器)


         底层结构通过将deque为底部结构并封闭器头端口,便可轻而易举形成了一个stack。实际上stack可以用list作为结构,将一端封死就行了。

1.stack提供的接口 

     stack提供的接口:包括得到stack的属性接口、stack的操作接口以及构造函数:

   (1)构造函数:stack()。

   (2)属性函数:size、empty、top。

   (3)操作函数:push、pop。

注:pop返回类型为void,所以经常以top 和pop一起使用。


2.stack的数据结构

template<class T,class sequece=deque<T> >
class stack{
protected:
   Sequence c;//所有的接口转到调用C的接口但是只操作一端
}


queue

    queue是一种先进后出(FIFO)的数据结构。它有两个出口,形式如图所示。stack允许新增元素、从底端移除元素、取得最顶元素。但除了底端可以加入,最顶端可以取出外,没有其他任何方法可以存取queue的其他元素。换言之,queue不允许有任何遍历行为。(queue没有迭代器)


         底层结构通过将deque为底部结构,改一下接口使其符合”先进先出“的特性。实际上queue可以用list作为结构,将一端封死就行了。

1.queue提供的接口 

     stack提供的接口:包括得到stack的属性接口、stack的操作接口以及构造函数:

   (1)构造函数:queue()。

   (2)属性函数:size、empty、front和back。

   (3)操作函数:push、pop。

注:pop返回类型为void,所以经常以front和pop一起使用。


2.queue的数据结构

template<class T,class sequece=deque<T>>
class queue{
protected:
   Sequence c;//这个跟上面不同的是两端不封死
}



heap和priority_queue

     heap以vector为底层接口,并可通过sift_up和sift_down进行堆调整。heap提供的接口:make_heap、sort_heap、push_heap、pop_heap。下面举一个例子:

#include<iostream>
#include<vector>
#include<algorithm> //必须包含这个头文件
using namespace std;


int main()
{	
   int a[5]={0,1,2,3,4};
   vector<int> ivec(a,a+5);
   make_heap(ivec.begin(),ivec.end());
   for(int i=0;i<ivec.size();++i)
	cout<<ivec[i]<<" ";
   system("pause");
   return 0;
}

     priority_queue有一个优先级的概念。它是利用一个make_heap完成,而heap又是以vector呈现。priority_queue提供的接口:

     构造函数:priority_queue(InputIterator first,InputIterator last,const Compare &x)

                       priority_queue(InputIterator first,InputIterator last)

     其他接口:size、empty、top、push和pop

template<class T,class sequece=vector<T>,class Compare = less<typename sequence::value_type> >
class queue{
protected:
   Sequence c;
   Compare comp;
}

    下面举一个例子,注意必须包括queue头文件。

#include<iostream>
#include<vector>
#include<queue>
//#include<algorithm>
using namespace std;


int main()
{
    int a[5]={0,1,2,3,4};
    priority_queue<int> ivec(a,a+5);
    while(!ivec.empty()){
        cout<<ivec.top()<<" ";
	ivec.pop();
    }
    system("pause");
    return 0;
}

slist

    slist是一种单链表结构,slist和list的差别:list提供的Bidirectional iterator迭代器,而slist提供的是Forward Iterator。slist和list共同具有的特色是他们的插入、移除、结合等操作并不会造成迭代器失效。插入操作会将新元素插入于指定位置之前,而非之后。这样slist每次插入都要从头遍历找到前一个节点。

1.slist提供的接口 

     slist提供的接口:包括得到slist的属性接口、slist的操作接口以及构造函数:

   (1)构造函数:slist()。

   (2)属性函数:begin、size、empty、front。

   (3)操作函数:front、pop_front、push_front


2.slist的数据结构

struct __slist_node_base{
    __slist_node_base *next;//可以作为头节点
}
template<class T>
struct __slist_node:public  __slist_node_base{
    T data;
}

struct __slist_iterator_base{
     typedef forward_iterator_tag iterator_category;
     __slist_node_base *node;
    ...
}
template<class T,class Ref,class Ptr>
struct  __slist_iterator: public __slist_iterator_base{
     typedef  __slist_iterator<T,T&,T*> iterator;
    ...
}

template<class T,class  Alloc=alloc>
class slist{
     typedef  __slist_node<T> list_node;
     typedef  __slist_iterator<T,T&,T*> iterator;
    ...
}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值