STL 源码剖析 deque实现源码

来源:互联网 发布:软件项目招标文件 编辑:程序博客网 时间:2024/05/18 20:11

#ifndef _HJSTL_DEQUE_H_#define _HJSTL_DEQUE_H_/*  * Author:hujian  * Time:2016/4/28  * discription:this file is about deque structure.  **/#include "hjstl_alloc.h"#include "hjstl_construct.h"#include "hjstl_iterator.h"#include "hjstl_uninitialized.h"#define _HJSTL_DEQUE_PUBLIC_  public#define _HJSTL_DEQUE_PRIVATE_ private #define _HJSTL_DEQUE_PROTECTED_ protected//deque buffer sizeinline size_t __HJSTL_deque_buf_size(size_t n, size_t sz){return n != 0 ? n : (sz < 512 ? size_t(512 / sz) : size_t(1));}//the deque iterator.this is very complex.template<class Type,class Ref,class Ptr,size_t BufSize/*you can change the size*/>struct __HJSTL_deque_iterator{    typedef __HJSTL_deque_iterator<Type, Ref&, Ptr*, BufSize>  iterator;typedef     __HJSTL_deque_iterator<Type, const Ref&, const Ptr*, BufSize> const_iterator;static size_t buffer_size(){//you want to set the buffer size return __HJSTL_deque_buf_size(BufSize, sizeof(Type));}typedef hjstl_random_access_iterator_tag hjstl_iterator_category;typedef Type  hjstl_value_type;typedef Ptr   hjstl_pointer;typedef Ref   hjstl_reference;typedef size_t  size_type;typedef ptrdiff_t hjstl_difference_type;typedef Type** map_pointer;typedef __HJSTL_deque_iterator self;Type* cur; //the iterator's  buffer's current element.Type* first;//the iterator's buffer's head elementType* last;//the iterator's buffer's tail elementmap_pointer node;//this node pointer to the controller.//constructor__HJSTL_deque_iterator(Type* x,map_pointer y):cur(x), first(*y), last(*y + buffer_size(), node(y)){}__HJSTL_deque_iterator():cur(0), first(0), last(0), node(0){}__HJSTL_deque_iterator(const iterator& x):cur(x.cur), first(x.first), last(x.last), node(x.node){}hjstl_reference operator*() const { return *cur; }hjstl_pointer  operator->() const { return &(operator*()); }hjstl_difference_type operator -(const self&x) const {return hjstl_difference_type(buffer_size())*(node - x.node - 1) +(cur - first) + (x.last - x.cur);}self& operator ++(){ //++iterator++cur;if (cur == last){set_node(node + 1);cur = first;}return *this;}self operator++(int) {//iterator++self tmp = *this;++*this;return tmp;}self& operator --(){//--iteratorif (cur == first){set_node(node - 1);cur = last;}--cur;return *this;}self operator --(int){//iterator--self tmp = *this;--*this;return tmp;}self& operator +=(hjstl_difference_type n){//iterator += nhjstl_difference_type offset = n + (cur - first);//ok,aim index is at the same buffer,just pointer to..if (offset >= 0 && offset < hjstl_difference_type(buffer_size)){cur += n;}else{//difference buffer.so,we need to do more thing hjstl_difference_type node_offset = offset>0/*forward*/ ?offset / hjstl_difference_type(buffer_size) :-hjstl_difference_type(-offset - 1) - 1;//goto the node.set_node(node + node_offset);//goto the cur we should.cur = first + (offset - node_offset*hjstl_difference_type(buffer_size()));}return *this;}//ok,we have implemented the +=,then the + is so easy to implementself operator +(hjstl_difference_type n) const {self tmp = *this;return tmp += n;}//yeah,do you know we can use add to implement minus..?self & operator -=(hjstl_difference_type n){return *this += -n;}//:>self operator -(hjstl_difference_type n) const {self tmp = *this;return tmp -= n;}//random put/gethjstl_reference operator[](hjstl_difference_type n) const{return *(*this + n);}bool operator ==(const self& x) const { return cur == x.cur; }bool operator !=(const self& x) const { return cur != x.cur; }bool operator<(const self&x)const {return (node == x.node) ? (cur < x.cur) : (node < x.node);}//set node.goto..void set_node(map_pointer new_node){node = new_node;first = *new_node;last = first + hjstl_difference_type(buffer_size());}};template<class Type,class Ref,class Ptr,size_t BufSz>inline hjstl_random_access_iterator_taghjstl_iterator_category(const __HJSTL_deque_iterator<Type, Ref, Ptr, BufSz>&){return hjstl_random_access_iterator_tag();}template<class Type,class Ref,class Ptr,size_t BufSz>inline Type* hjstl_value_type(const __HJSTL_deque_iterator<Type, Ref, Ptr,BufSz>&){return 0;}template<class Type, class Ref, class Ptr,size_t BufSz>inline ptrdiff_t* hjstl_distance_type(const __HJSTL_deque_iterator<Type, Ref, Ptr, BufSz>&){return 0;}//the follow codes is about the deque.//you can set the block's size,the default block size is 512 bytestemplate<class Type,class Alloc=HJSTL_Alloc::HJSTL_Allocate_default,size_t BufSize=0>class _HJSTL_deque{_HJSTL_DEQUE_PUBLIC_:typedef Type   value_type;typedef value_type* pointer;typedef const value_type* const_pointer;typedef value_type& reference;typedef const value_type& const_reference;typedef size_t  size_type;typedef ptrdiff_t difference_type;//the deque's iterator.typedef __HJSTL_deque_iterator<Type, Type&, Type*, BufSize> iterator;typedef __HJSTL_deque_iterator<Type, const Type&, const Type*, BufSize> const_iterator;_HJSTL_DEQUE_PROTECTED_://pointer of pointer of Type.typedef pointer* map_pointer;//the data allocator.typedef _HJSTL_simple_alloc<value_type, Alloc> data_allocator;//map node allocatortypedef _HJSTL_simple_alloc<pointer, Alloc> map_allocator;//get the buffer sizestatic size_type buffer_size(){return __HJSTL_deque_buf_size(BufSize, sizeof(value_type));}//the initialze map size(the nodes of the map in the beginning)static size_type initial_map_size(){return 8;//you can change here to adjust your map nodes.}//the data member.iterator start;//the first node of map.iterator finish;//the last node of map//the map.this is an array.stored the node pointer.//and the node is pointer too.map_pointer map;//the map sizesize_type  map_size;_HJSTL_DEQUE_PUBLIC_://get member.iterator begin(){ return start; }iterator end(){ return finish; }const iterator begin() const { return start; }const iterator end() const { return finish; }reference operator[](size_type n){return start[difference_type(n)];}const reference operator[](size_type n) const{return start[difference_type(n)];}reference front(){return *start;//the first node's first element.}reference back(){iterator tmp = finish;//the finish is empry.--tmp;return *tmp;}const reference front() const{return *start;//the first node's first element.}const reference back() const{const_iterator tmp = finish;//the finish is empry.--tmp;return *tmp;}//the size of this eque    //NOTICE:the operator '-' is not so simple.you need to know that.size_type size()const{ return finish - start; }//-1 is max number...size_type max_size()const { return size_type(-1); }//is empty?bool empty(){ return start == finish; }_HJSTL_DEQUE_PUBLIC_://the follow is constructor_HJSTL_deque() : start(), finish(), map(0){//just set up an default deque for user.create_map_and_nodes(0);}_HJSTL_deque(const _HJSTL_deque&x):start(), finish, map(0), map_size(0){create_map_and_nodes(x.size());try{uninitialized_copy(x.begin(), x.end(), start);}catch (...){//errordestroy_map_and_nodes();}}_HJSTL_deque(size_type n, const value_type& value):start(), finish(), map(0), map_size(0){fill_initialze(n, value);}_HJSTL_deque(int n, const value_type& value):start(), finish(), map(0), map_size(0){fill_initialze(n, value);}_HJSTL_deque(long n, const value_type& value):start(), finish(), map(0), map_size(0){fill_initialze(n, value);}explicit _HJSTL_deque(size_type n):start(), finish(), map(0), map_size(0){fill_initialze(n, value_type());}template<class InputIterator>_HJSTL_deque(InputIterator first, InputIterator last): start(), finish(), map(0), map_size(0){range_initialze(first, last, hjstl_iterator_category(first));}_HJSTL_deque(const value_type* first, const value_type* last):start(), finish(), map(0), map_size(0){create_map_and_nodes(last-first);try{uninitialized_copy(first, last, start);}catch (...){destroy_map_and_nodes();}}_HJSTL_deque(const_iterator first, const_iterator last):start(), finish(), map(0), map_size(0){create_map_and_nodes(last - first);try{uninitialized_copy(first, last, start);}catch (...){destroy_map_and_nodes();}}//deconstructor~_HJSTL_deque(){destroy(start, finish);destroy_map_and_nodes();}//function._HJSTL_deque& operator =(const _HJSTL_deque& x){const size_type len = size();if (&x != this){//difference dequeearse(copy(x.begin(), x.end(), start), finish);}else{const_iterator mid = x.begin() + difference_type(len);copy(x.begin(), mid, start);insert(finish, mid, x.end());}return *this;}//erase an iteratoriterator erase(iterator pos){iterator next = pos;++next;difference_type index = pos - start; //the length of before remove element.if (index < (size() >> 1/*just like size()/2*/)){//less copy_backward(start, pos, next);pop_front();//delete the pos's value}else{//more.copy(next, finish, pos);pop_back();//delete the pos'value}//return the pos's valuereturn start + index;}//erase the range [first,last)iterator erase(iterator first, iterator last){//if you want to clear the deque.just use clearif (first == start&&last == finish){clear();return finish;}else{difference_type len = last - first;difference_type elements_before = first - start;if (elements_before < (size() - len) / 2)//the before elements less{copy_backward(start, first, last);//the new startiterator new_start = start + len;//destructor the elemenst we do not want to leftdestroy(start, new_start);//back the memory to the memeory poolfor (map_pointer cur = start.node; cur < new_start.node; ++cur){data_allocator::deallocate(*cur, buffer_size());}//set the startstart = new_start;}else{//the length of range [first,last) ->before>aftercopy(last, finish, first);iterator new_finish = finish - len;destroy(new_finish, finish);for (map_pointer cur = new_finish.node + 1; cur <= finish.node; ++cur){data_allocator::deallocate(*cur, buffer_size());}//set the new finishfinish = new_finish;}return start + elements_before;}}//insert an item into the deque.//this is the insert auxiterator insert_aux(iterator pos, const value_type& value){difference_type index = pos - start;value_type copy_v = value;if (index < size() / 2){//before<after//insert into the front values(front()) push_back(front());//move the elements.iterator front_1 = start;++front_1;iterator front_2 = front_1;++front_2;//re get-the pos's iteratorpos = start + index;iterator pos_1 = pos;++pos_1;//copy data.copy(front_2, pos, front_1);}else{//before>=afterpush_back(back());iterator back_1 = finish;--back_1;iterator back_2 = back_1;--back_2;pos = start + index;copy(pos, back_2, back_1);}//assignment.*pos = copy_v;return pos;}//this is the insert iterator insert(iterator position, const value_type& value){//front or back?if (position.cur == start.cur){push_front(value);return start;}else if (position.cur == finish.cur){push_back(value);iterator tmp = finish;--tmp;return tmp;}else{return insert_aux(position, value);}}//insertiterator insert(iterator position){//empty initialzereturn insert(position, value_type());}//realloc the mapvoid reallocate_map(size_type nodes_to_add, bool add_at_front/*opt*/){size_type old_num_nodes = finish - start + 1;size_type new_num_nodes = old_num_nodes + nodes_to_add;map_pointer new_nstart;if (map_size > 2 * new_num_nodes){new_nstart = map + (map_size - new_num_nodes) / 2 +(add_at_front ? nodes_to_add : 0);if (new_nstart < start.node){copy(start.node, finish.node + 1, new_nstart);}else{copy_backward(start.node, finish.node + 1,new_nstart+ old_num_nodes);}}else{size_type new_map_size = map_size + (map_size > nodes_to_add ? map_size : nodes_to_add) + 1;map_pointer new_map = map_allocator::allocate(new_map_size);new_nstart = new_map + (new_map_size - new_num_nodes) / 2 +(add_at_front ? nodes_to_add : 0);copy(start.node, finish.node + 1, new_nstart);map_allocator::deallocate(map, map_size);//set the new map.attrmap = new_map;map_size = new_map_size;}start.set_node(new_nstart);finish.set_node(new_nstart + old_num_nodes + 1);}//reserve map at back//default add 1 nodevoid reserver_map_at_back(size_type nodes_to_add = 1){if (nodes_to_add + 1 > map_size - (finish.node - map)){reallocate_map(nodes_to_add, false);}}void reserver_map_at_front(size_type nodes_to_add = 1){if (nodes_to_add > start.node - map){reallocate_map(nodes_to_add, true);}}//just swapvoid swap(_HJSTL_deque& x){std::swap(start, x.start);std::swap(finish, x.finish);std::swap(map, x.map);std::swap(map_size, x.map_size);}//push_backvoid push_back(const value_type& value){if (finish.cur != finish.last - 1){construct(finish.cur, value);++finish.cur;}else{//i let the another function do thispush_back_aux(value);}}//push_back_auxvoid push_back_aux(const value_type& value){value_type copy_v = value;reserver_map_at_back();//alloc an new node*(finish.node + 1) = allocate_node();try{construct(finish.cur, copy_v);finish.set_node(finish.node + 1);finish.cur = finish.first;}catch (...){//if error,just deallocate the alloc-ed memory.deallocate_node(*(finish.node + 1));}}//push_front_auxvoid push_front_aux(const value_type& value){value_type copy_v = value;reserver_map_at_front();//allocate an new node *(start.node-1)=allocate_node();try{start.set_node(start.node - 1);start.cur = start.last - 1;construct(start.cur, copy_v);}catch (...){//errorstart.set_node(start.node + 1);start.cur = start.first;deallocate_node(*(start.node - 1));}}//push_frontvoid push_front(const value_type& value){if (start.cur != start.first){construct(start.cur, value);--start.cur;}else{push_front_aux(value);}}//pop_front_aux//edge...void pop_front_aux(){destroy(start.cur);deallocate_node(start.first);start.set_node(start.node + 1);start.cur = start.first;}//pop_back_aux//edgevoid pop_back_aux(){deallocate_node(finish.first);finish.set_node(finish.node - 1);finish.cur = finish.last - 1;destroy(finish.cur);}//pop_backvoid pop_back(){if (finish.cur != finish.first){--finish.cur;destroy(finish.cur);}else{pop_back_aux();}}//pop_frontvoid pop_front(){if (start.cur != start.last - 1){destroy(start.cur);++start.cur;}else{pop_front_aux();}}_HJSTL_DEQUE_PROTECTED_://some useful function//allocate an nodepointer allocate_node(){return data_allocator::allocate(buffer_size());}//deallocate an nodevoid deallocate_node(pointer ptr){data_allocator::deallocate(ptr, buffer_size());}//set up a deque with num_elements.void create_map_and_nodes(size_type num_elements){//the needed nodessize_type num_nodes = num_elements / buffer_size() + 1;//get the map sizemap_size = initial_map_size()> (num_nodes+2) ? initial_map_size() : num_nodes+2;//alloc map_size's nodes for map.map = map_allocator::allocate(map_size);//in the mid.map_pointer nstart, nfinish;nstart = map + (map_size - num_nodes) / 2;nfinish = nstart + num_nodes - 1;map_pointer cur;try{//alloc for each nodefor (cur = nstart; cur<=nfinish; cur++){*cur = allocate_node();}}catch (...){//deallocate.for (map_pointer n = nstart; n < cur; n++){deallocate_node(*n);}map_allocator::deallocate(map, map_size);}//set the start and finishstart.set_node(nstart);finish.set_node(nfinish);start.cur = start.first;//make sure you can understand the follow code.finish.cur = finish.first + num_elements%buffer_size();}//destroy map and nodesvoid destroy_map_and_nodes(){for (map_pointer cur = start.node; cur <= finish.node; ++cur){deallocate_node(*cur);}map_allocator::deallocate(map, map_size);}//fill initialzevoid fill_initialze(size_type n, const value_type& value){create_map_and_nodes(n);map_pointer cur;try{for (cur = start.node; cur < finish.node; ++cur){uninitialized_fill(*cur, *cur + buffer_size(), value);}//finish node initialzeuninitialized_fill(finish.first, finish.cur, value);}catch (...){//if not succeed,destory the old..for (map_pointer n = start.node; n < cur; ++n){destroy(*n, *n + buffer_size());}destroy_map_and_nodes();}}//range initialzetemplate<class InputIterator>void range_initialze(InputIterator first, InputIterator last, hjstl_input_iterator_tag){create_map_and_nodes(0);while (first != last){push_back(*first++);}}template<class ForwardIterator>void range_initialze(ForwardIterator first, ForwardIterator last, hjstl_forward_iterator_tag){size_type n = 0;hjstl_distance(first, last, n);create_map_and_nodes(n);try{uninitialized_copy(first, last, start);}catch (...){destroy_map_and_nodes();}}_HJSTL_DEQUE_PUBLIC_://clear this deque//we need to left one buffer for this empty deque.//void clear(){for (map_pointer node = start.node + 1; node < finish.node; ++node){destroy(*node, *node + buffer_size());data_allocator::deallocate(*node, buffer_size());}//if we have at least 2 buffer left,just left the head bufferif (start.node != finish.node){destroy(start.cur, start.last);destroy(finish.first, finish.cur);data_allocator::deallocate(finish.first, buffer_size());}//else,just one buffer left now.//NOTICE:do not back the memory to memory pool.//this is the must-left buffer in an empty deue.else{destroy(start.cur, finish.cur);}//set the finishfinish = start;//means this is an empty deque,you can look up the//function empty() to check the algorithm}};//end of hjstl deque///<2016/4/28 hujian deque>#endif //end of _HJSTL_DEQUE_H_

      拖了很久,前面写vector和list的时候因为有些地方需要重新改动,比如要去修改(增加特性支持)迭代器部分或者内存管理部分。写完vector之后应该说整个流程都打通了,接下来就是填代码了。

     在写list的时候,主要是为了强化对链表这种数据结构的认识。确实,如果能把list里面的链表操作搞明白了,我觉得对于链表可以说是掌握了。

     特别是,list实现时采用的是循环双链表,所以只要你明白了他的运行原理及结构,那么什么单向链表,双向链表,循环链表都不在话下。

     那如果说重写list最大的收获是加深了对链表的理解,重写deque带来了什么呢?

    看了源码之后,你会发现deque是所有容器中代码量最多的。这也从一个侧面反映出了deque的复杂性。

    但是,deque不是采用链表,而是线性表。和vector很像,只是vector只能单方向操作,而deque支持头尾操作,而且效率很高。

    deque的复杂性在于他的迭代器设计和内存管理,特别是迭代器设计,是整个deque设计过程中的重点和难点。因为deque是一段一段的空间组成的

   “连续”空间,所以,对于接口来说,需要实现像vector一样的自然操作,那vector用的是线性表,没什么复杂的,但是deque不是线性表,deque一方面解决了

   vector内存生长时的复杂度之外,还加深了存取操作,但随之而来的是加大了设计迭代器的复杂性。

   关于deque如何设计迭代器与如何管理内存,可以在我自己整理的文件中看到,关于整个文件,我将把从开始到现在实现的所有文件和说明文档都发到

   github,下一步有兴趣的同学可以下载来看看......

    因为大家可能在windows下看的,直接编译SGI的STL需要花很大的功夫,还不一定能成功,我写的你不仅可以在windows下编译,还可以在linux下编译,不仅

  平台无关,还编译器无关~~~(对于需要做少量修改的地方,大家自行处理)


   ok,下面是源码,我会在下一篇文章中贴出github地址,大家可以下载。


0 0