STL之序列式容器难点分析

Vector容器

又称作变长数组,随着元素的增加,其内部机制会自行扩充空间以容纳新元素。

其实现的原理是:在vector中有三个容器分别表示容器目前使用空间的头,尾和可用空间的尾。即容器分配的内存要大于等于实际使用的内存。

如果vector无使用内存的话,容器会重新分配一块更大的空间,然后将数据移动到新的空间,然后将旧空间释放掉。

所以vector有两个成员函数,size()代表使用空间的大小;capacity()代表可用空间的大小


由于vector采用的连续空间,所以vector的迭代器是原型指针。

template<Class T, class Alloc= alloc>

class vector{

public:

typedef T value_type;

typedef value_type*  iterator;

}

vector最关键的技术是insert函数

template<class T, class Alloc>

void vector<T, Alloc>::insert_aux(iterator position, const T& x){

if(finish != end_of_storage){  //还有备用空间

construct(finish , *(finish - 1));

//调整水位

++finish;

T x_copy = x;

copy_backward(position, finish - 2, finish - 1);// 将[first, last )区间内的每一个元素,即[position, finish-2),以逆行的方向复制到以result-1即(finish -1)起点的区间

*position = x_copy;

}

else

{

const size_type old_size = size();

const size_type len = old_size != 0 ? 2 * old_size : 1; //如果空间分配不足,按照2倍空间分配

iterator new_start = data_allocator::allocate(len);

iterator new_finish = new_start;

try{

new_finish = uinitalized_copy(start , position,new_start);

construct(new_finish,x);

++new_finish;

new_finish = uinitalized_copy(position + 1, finish, new_finish);'

}

catch{

destroy(new_start, new finish);

data_alloctor::deallocate(new_start, len);

}

}

destory(begin(),end());

deallocate();


start = new_start;

finish = new_finish;

end_of_storage = new_start + len;

}

//由于vector 成员函数的操作中使用到了copy 函数,需要对copy函数进行解析

copy函数---强化效率无所输入不用其极,模板偏特化,函数重载,型别特性等

copy函数:将输入区间[first ,last) 内元素复制到输出区间[result, result + (last - first)) copy算法对template参数所要求的条件非常宽松,

其输入区间只需由InputIterator,输出区间只需由OutputIterator,拷贝算法特别通用。

copy 完全泛化版本:

template<class InputIterator ,  class OutputIterator>

inline OutputIterator copy(InputIterator first, InputIterator last, OutputIterator result)

{

return _copy_dispatch<InputIterator , OutputIterator > ()(first, last , result);  // 仿函数临时对象

}

//特化版本,采用的重载技术

inline char* copy(char * first ,  char * last , char * result )

{

memmove(result,  first , last - first);

return result + (last - first);

}

inline wchar* copy(wchar * first ,  wchar * last , wchar * result )

{

memmove(result,  first , last - first);

return result + (last - first);

}


//泛化版本的仿函数

template<class InputIterator , class OutputIterator >

struct _copy_dispatch

{

OutputIerator operator()(InputIterator  first, InputIterator last, OutputIterator result){

return _copy(first ,last ,result , Iterator_category_category(first));

}

};

//特化版本的仿函数

template<T> 

struct _copy_dispatch<T* ,T*>

{

T* operator()(T* first , T* last, T* result)

{

typedef typename _type_traits<T>::has_trivial_assignment_operator t;

return _copy_t(first , last , result , t());  //特化版本一种是通过memmove 、 _copy_d

}

};

template<T> 

struct _copy_dispatch<const T* ,T*>

{

T* operator()(T* first , T* last, T* result)

{

typedef typename _type_traits<T>::has_trivial_assignment_operator t;

return _copy_t(first , last , result , t());

}

};


//重载函数根据迭代器的类型

template<class InputIterator , class OutputIterator>

inline OutputIterator _copy(InputIterator first, InputIterator last, OutputIterator result, input_iterator_tag)

{

for(; first ! = last ; last++)

{

*result = *first;

}

return result;

}


template<class RandomAccessIterator , class OutputIterator>

inline OutputIterator _copy(RandomAccessIterator first, RandomAccessIterator last, OutputIterator result, random_access_iterator_tag)

{

return _copy_d(first , last , result , distance_type(first) );

}


temlate<class RandomAccessIterator , class OutputIterator, class Distance>

inline OutputIterator _copy_d(RandomAccessIterator first, RandomAccessIterator last , OutputIterator result, Distance*)

{

for(Distance n = last - frist ; n > 0;--n ,++result, ++first){

*result = * first;

}

return result;

}


list容器:

list容器是一个普通数据结构中的双向链表。

其节点的结构如下:

template<class T>

struct _list_node{

typedef void*  void_pointer;

void_pointer * pre;

void_pointer * next;

T  data;

}

list实现采用的是环形的双向链表,所以list的迭代器类型为Bidirectional Iterators

_list_iterator的设计其中必须存在一个执行list节点的指针即

template<class T, class Ref, class Ptr>

struct _list_iterator

{

typedef  _list_iterator<T,Ref,Ptr> self; //为了实现迭代器之间的比较

typedef  _list_node<T> * link_type;

link_type node;//用于接收指针,递增,递减,比较等操作

}

list操作可以参考下,作为设计链表的参考代码:

链表的插入:

template<class T>

iterator insert(iterator position , const T& x)

{

link_type tmp = create_node(x);

tmp->next = position.node;  //新节点的next 指向postion节点

tmp->pre = position.node->pre;//新节点的pre执行position节点的pre

(link_type(position.node)->pre)->next =  tmp;// position节点的pre的next指向tmp

position.node->pre = tmp;// position的前驱pre指向tmp

return tmp;

}

链表的删除:

iterator erase(iterator position){

link_type pre_node = link_type(position.node->pre);

link_type next_node = link_type(position.node->next);

pre_node->next = next_node;

next_node->pre = pre_node;

destroy(positon.node);

return iterator(next_node);

}


///deque 是一种双向开口的连续线性空间。

deque的特点:允许于常数时间内对起头端进行插入或移动操作;deque没有所谓的容量观念。

deque实现原理:

deque采用一块所谓的map作为主控,这里所谓map是一小块连续空间,其中每个元素都是指针,指向另一段连续线性空间,称为缓冲区。

template<class T, class Alloc = alloc, size_t BufSiz = 0>

class deque{

public :

typedef T value_type;   //数值类型

typedef value_type* pointer;//指针

typedef size_t size_type;


typedef _deque_iterator<T, T&,T*,BufSiz> iterator;

protected:

typedef pointer* map_pointer;//指针的指针

iterator  start;

iterator finish;

map_pointer  map;   //中控器

size_type  map_size; //管理内存指针的大小

}


template<class T, class Ref, class Ptr, size_t BufSiz>

struct _deque_iterator{

typedef _deque_iterator<T, T&, T*,BufSize> iterator;

typedef _deque_iterator<T, const T&,const T*,BufSiz> const_iterator;

static size_t buffer_size(){return _deque_buf_size(BufSiz, sizeof(T));}


typedef random_access_iterator_tag iterator_category;

typedef T value_type;

typedef Ptr pointer;

typedef Ref reference;

typedef size_t size_type;

typedef ptrdiff_t difference_type;

typedef T** map_pointer;


typedef _deque_iterator self;

T*  cur;

T* first;

T* last;

map_pointer node;

}

deque实现的关键是通过迭代器的operator操作符,接下来一一分析

void set_node(map_pointer  new_node)

{

node = new_node;

first  =  * new_node;

last = first + difference_type(buff_size());

}


reference operator* () const { return *cur};

pointer operator->()  const {  return &(operator*() ) ; }


difference_type operator- (const self &) const {

return difference_type(buffer_size()) * (node - x.node + 1) + (cur - first ) + (x.last - x.cur);

}

self& operator++(){   //前置式,先加

++cur;

if(cur == last){ //加完后发现本节点的无内存可用

set_node(node + 1); //移动到下一个节点

cur = first; //令当前节点等于第一个节点,迭代器设计

}

return * this;

}

self operator++(int)//后置式标准写法

{

self tmp = *this; //返回本迭代器节点

++*this;               //采用前置法

return tmp;

}

self& operator--()

{

if(cur == first)

{

set_node(node -1);

cur =last;

}

--cur;

return *this;

}

self operator--(int)

{

self tmp = *this;

--*this;

return tmp;

}

self& operator +=(difference_type n)//跳跃多个距离,n可以为负值

{

difference_type offset = n + (cur - first);

if(offset >= 0 && offset < difference_type(buffer_size())

cur += n;

else{

difference_type node_offset = offset > 0 ? offset / difference_type(buffer_size()) : -difference_type((-offset -1) / buffer_size() )  - 1;

set_node(node + node_offset);

cur = first + (offset - node_offset * difference_type(buffer_size()));

}

return *this;

}

self operator+(difference_type n)

{

self tmp = *this;

return tmp += n;

}

self operator-=(difference_type n)

{

return *this +=  -n;

}

self operator-(difference_type n)

{

self tmp = *this;

return tmp -=  n;

}

reference operator[](difference_type n) const  {return *(*this + n); }

//迭代器的上述操作,能够满足deque的插入,访问等。如果空间不足的时候 需要调整中控器的大小。


//stack栈

stack是一种配接器,它是以改变底层容器的接口为实现,称为配接器。

由于栈只能先进后出的数据结构,可以通过deque和list作为底层实现的容器,其实用vector也可以实现。


//queue队列

队列是一种先进先出的结构

queue也是一种配接器,也是通过底层容器实现,可以由deque和list为底层实现的容器,其实用vector也可以实现


//heap堆

完全二叉树,可以利用array来存储所有节点。将array的0号元素保留,那么某个节点位于array的i处,左子节点 必位于2i处,右子节点位于2i+1处。

我们采用vector作为存储介质,然后配合heap的插入,删除,取极值等算法组成。

//插入算法,保持堆的特性

template<class RandomAccessIterator>

inline void push_heap(RandomAccessIterator first , RandomAccessIterator last)

{

_push_heap_aux(first,  last , distance_type(first),  value_type(first) );

}

template<class RandomAccessIterator, class Distance, class T>

inline void _push_heap_aux(RandomAccessIterator first, RandomAccessIterator last, Distance* , T*)

{

_push_heap(first, Distance((last - first) - 1), Distance(0) ,  T(*(last - 1)) );

}

template<class RandomAccessIterator , class Distance, class T>

inline void _push_heap(RandomAccessIterator first, Distance holeIndex ,  Distance topIndex,  T value)

{

Distance parent = (holeIndex - 1) / 2;   //找到父节点

while(holeIndex > topIndex &&  *(first + parent) <  value) //判断当前节点是否为头结点,以及 当前节点的值是否大于父节点的值

{

*(first + holeIndex) =  *(first +parent);    //移动父节点的值

holeIndex = parent;                                  //更换holeIndex的位置

parent = (holeIdex  - 1 )/ 2;                     //重新计算父节点的位置

  }

*(first + holeIndex) =  value;                                //将新值插入

}


//删除算法,保持堆的特性

template<class RandomAccessIterator , class Distance, class T>

void _adjust_heap(RandomAcessIterator first, Distance holeIndex, Distance len , T value)

{

Distance topIndex = holeIndex;   //保留topIndex

Distance secondChild = 2 * holeIndex + 2; //取右子节点

while(secondChild < len)   //小于最大长度

{

if(*(first + secondChild)  <  *(first + secondChild  -1 ))// 比较子节点大小

sencondChild--;

*(first + holeIdex) = *(first + secondChild);  //将子节点移动到移除节点位置

holeIndex = secondChild;

secondChild = secondChild * 2 + 2;

if(secondChild == len)

{

*(first + holeIndex) = *(first  + secondChild  -1 );

holeIndex = secondChild - 1;

}

_push_heap(first, holeIndex , topIndex, value);

}


//优先级队列priority_queue

优先级队列是以heap为底层容器的配接器。


//slist单向链表

单向链表与双向链表相比的特点:

1. 迭代器是InputIterator 单向迭代器

2. 类只是先了push_front 和popfront,因为使用insert插入只能插在其前面不能插在后面

3. 迭代器使用继承,没看懂好处在哪里?

猜你喜欢

转载自blog.csdn.net/w1012747007/article/details/76222798