序列式容器:
array(build-in)、vector、heap(以算法形式呈现)、priority-queue、list、slist(非标准)、deque、stack(配接器)、queue(配接器)
所谓序列式容器,其中的元素都可序,但是未必有序。
---------------------------------------------------------------------------------------------------
vector:
vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。
vector的迭代器:
vector的迭代器是普通指针,其型别是Random Access Iterators:
template<class T ,class Alloc = alloc> class vector{ public : typedef T value_type ; typedef value_type* iterator ; ;//vector的迭代器是普通指针 ... } ;
vector采用的数据结构:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端(vector本身就是内含这三个东东):
template<class T,class Alloc = alloc> class vector{ ... protected : iterator start ; //表示目前使用空间的头 iterator finish ; // 表示目前使用空间的尾 iterator end_of_storage ; //表示目前可用空间的尾 }
PS:由于经常在论坛上面逛,发现有很多人对vector的认识进了一个误区:1、vector不是用自身来储存元素的。2、一个空的vector,经sizeof(vector)的结果应该为0。
第一点:首先从书上的源码来看,vector并不是用自身来储存数据的,要是这样的话vector会变得很大。vector只是从堆那里获得内存,并用三个迭代器分别指向这块内存相应的位置,并进行管理。其次,如果用vector自身来储存元素,也难以达到重新配置空间等目的。
第二点:一个空的vector,并不指是vector本身是空的,指的是vector没有管理一块用于储存元素的内存。vector本身的大小是固定的,它内含三个迭代器,分别是start、finish、end_of_storage外加由于内存对齐需要填充的空位,以上组成了vector的大小。
------------------------------------------------------------------------------------------------
list:
list的节点(node):
list本身和list的节点是不同的结构,需要分开设计。以下是STL list的节点(node)结构:
template<class T> struct __list_node{ typedef void *void_pointer ; void_pointer prev ; //型别为void * 。其实可设为__list_node<T>* void_pointer next ; T data ; } //由此结构可以看出,这是一个双向链表
由于STL list是一个双向链表(double linked-list),迭代器必须具备前移、后移的能力,所以list提供的是Bidirectional Iterators。
一个重要的性质:插入操作和接合操作都不会造成原有的list迭代器失效。甚至list的元素删除操作也只有“指向被删除元素”的那个迭代器失效,甚至迭代器不受任何影响。
template<class T,class Ref,class Ptr> struct __list_iterator<T,T&,T*>{ typedef __list_iterator<T,T&,T*> iterator ; typedef __list_iterator<T,Ref,Ptr> self ; typedef bidirectional_iterator_tag iterator_category ; typedef T value_type ; typedef Ptr pointer ; typedef Ref reference ; typedef __list_node<T>* link_type ; typedef size_t size_type ; typedef ptrdiff_t difference_type ; link_type node ; //迭代器内部当然要有一个普通指针,指向list的节点 //construct __list_iterator(link_type x) : node(x) {} __list_iterator() {} __list_iterator(const iterator &x):node(x.node) {} bool operator==(const self &x) const { return node == x.node ;} bool operator!=(const self &x) const { return node != x.node ;} //以下对迭代器取值(dereference),取的是节点的数据值 reference operator*() const { return (*node).data ;} //以下是迭代器的成员存取(member access)运算子的标准做法 pointer operator->() const { return &(operator*()) ;} //对迭代器累加1,就是前进一个节点 self& operator++(){ node = (link_typde)((*node).next) ; return *this ; } self& operator++(int){ self tmp = *this ; ++*this ; return tmp ; } //对迭代器递减1,就是后退一个节点 self& operator--(){ node = (link_type)((*node).prev) ; return *this ; } self& operator--(int){ self tmp = *this ; --*this ; return tmp ; } }
SGI list不仅是一个双向链表,而且还是一个环状双向链表。所以它只需要一个指针,便可以完整表现整个链表:
template<class T,class Alloc = alloc> //缺省使用alloc为配置器:w class list{ protected : typedef __list_node<T> list_node ; public : typedef list_node* link_type ; protected : link_type node ; //只要一个指针,便可以表示整个环状双向链表 };
由于node的作用,以下几个函数便都可以轻易完成:
iterator begin() {return (link_type)((*node).next) ;} iterator end() {return node ;} bool empty() const {return node->next == node ;} size_type size() const { size_type result = 0 ; distance(begin(),end(),result) ; return result ; } //取头节点的内容(元素值) reference front() {return *begin() ;} //取尾节点的内容(元素值) reference back() {reeturn *(--end()) ;}
--------------------------------------------------------------------------------------------
deque:
vector是单向开口的连续线性空间,deque则是一种双向开口的连续线性空间。所谓双向开口,意思是可以在头尾两端分别做元素的插入和删除操作。
deque和vector的最大差异,一在于deque允许于常数时间内对起头端进行元素的插入或移除操作,二在于deque没有所谓的容量(capacity)观念,因为它是动态地以分段连续空间组合而成,随时可以增加一段新的空间并链接起来。换句话说,像vector那样“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”这样的事情在deque是不会发生的。也因此,deque没有必要提供所谓的空间保留(reserve)功能。(事实deque也会“因旧空间不足而重新配置一块更大空间,然后复制元素,再释放旧空间”,只不过是进行这些操作的对象不是被储存的元素,而是作为中控器的map)。
deque的中控器:
deque系由一段一段的定量连续空间构成。一旦有必要在dequer前端或尾端增加新空间,便配置一段定量连续空间,串接在整个deque的头端或尾端。deque采用一块所谓的map作为主控。这里所谓map是一小块连续空间,其中每个元素(此处称为一个节点,node)都是指针,指向另一段(较大的)连续线性空间,称为缓冲区。缓冲区才是deque的储存空间主体。
template<class T, class Alloc = alloc, size_t BufSiz = 0>
class deque{
public :
typedef T value_type ;
typedef value_type* pointer ;
...
protected :
//元素的指针的指针(pointer of pointer of T)
typedef pointer* map_pointer ; //其实就是T**
protected :
map_pointer map ; //指向map,map是块连续空间,其内的每个元素
//都是一个指针(称为节点),指向一块缓冲区
size_type map_size ;
...
} ;
图:
deque的迭代器:
template<class T, class Ref, class Ptr, size_t BufSiz> struct __deque_iterator{ //未继承std::iterator typedef __deque_iterator<T,T&,T*,BufSize> iterator ; typedef __deque_iterator<T,const T&,const T*,BufSize> const_iterator ; static size_t buffer_size() {return __deque_buf_size(BufSize,sizeof(T)) ;} //未继承std::iterator,所以必须自行撰写五个必要的迭代器相应型别 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 *cut ; //此迭代器所指之缓冲区中的现行(current)元素 T *first ; //此迭代器所指之缓冲区的头 T *last ; //此迭代器所指之缓冲区的尾(含备用空间) map_pointer node ; //指向管控中心 ... }
deque的数据结构:
deque除了维护一个先前说过的指向map的指针外,也维护start,finish两个迭代器,分别指向第一缓冲区的第一个元素和最后缓冲区的最后一个元素(下一个位置)。
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 ;
public :
typedef __deque_iterator<T,T&,T*,BufSiz> iterator ;
protected :
//元素的指针的指针(pointer of pointer of T)
typedef pointer* map_pointer ;
protected:
iterator start ; //表现第一节点
iterator finish ; //表现最后一个节点
map_pointer map ; //指向map,map是块连续空间,其每个元素都是个指针,指向一个节点(缓冲区)
size_type map_size ; //map内有多少指针
...
} ;
--------------------------------------------------------------------------------------------
stack:
stack是一种先进后出(First In Last Out,FILO)的数据结构,它只有一个出口。stack允许新增元素,移除元素、取得最顶端元素。但除了最顶端外,没有任何其它方法可以存取stack的其它元素。换言之,stack不允许有遍历行为。
由于stack系以底部容器完成其所有工作,而具有这种“修改某物接口,形成另一种风貌”之性质者,称为adapter(配接器)。
template<class T, class Sequence = deque<T> > class stack{ friend bool operator== __STL_NULL_TMPL_ARGS(const stack& , const stack&) ; friend bool operator< __STL_NULL_TMPL_ARGS(const stack& , const stack&) ; public : typedef typename Sequence::value_type value_type ; typedef typename Sequence::size_type size_type ; typedef typename Sequence::reference reference ; typedef typename Sequence::const_reference const_reference ; protected: Sequence e ; //底层容器 public : //以下完全利用Sequence c 的操作,完成stack的操作 bool empty() const {return c.empty() ;} size_type size() {return c.size();} reference top() {return c.back();} const_reference top() const {return c.back();} //deque是两头可进出,stack是末端进,末端出。 void push(const value_type& x) {c.push_back(x) ;} void pop() {c.pop_back() ;} } ;除了以deque作为底层容器之外,list也可以作为stack的底层容器。
--------------------------------------------------------------------------------------------
queue:
queue是一种先进先出(First In First Out,FIFO)的数据结构。它有两个出口。queue允许新增元素、移除元素、从最底端加入元素、取得最顶端元素。与stack相似,queue不允许遍历行为,因此也没有迭代器。
template<class T, class Sequence = dique<T> > class queue{ public : typedef typename Sequence::value_type value_type ; typedef typename Sequence::size_type size_type ; typedef typename Sequence::reference reference ; typedef typename Sequence::const_reference const_reference ; protected : Sequence c ; //底层容器 public : //以下完全利用Sequence c的操作,完成queue的操作 bool empty() const {return c.empty();} size_type size() const {return c.size();} reference front() const {return c.front();} const_reference front() const {return c.front();} //deque是两头可进出,queue是末端进,前端出。 void push(const value_type &x) {c.push_back(x) ;} void pop() {c.pop_front();} } ;除了以deque作为底层容器之外,list也可以作为queue的底层容器。
-----------------------------------------------------------------------------------------------
heap:
heap并不归属于STL容器组件,它是个幕后英雄,扮演priority queue的助手。binary max heap适合作为priority queue的底层机制。
complete binary tree是一个vector/array和一组heap算法构成。
heap测试实例:
#include<vector> #include<iostream> #include<algorithm> //heap algorithm using namespace std ; int main(void) { { //test heap(底层以vector完成) int ia[9] = {0,1,2,3,4,8,9,3,5}; vector<int> ivec(ia,ia+9) ; make_heap(ivec.begin(),ivec.end()) ; for(int i = 0 ; i < ivec.size() ; ++i) { cout << ivec[i] << ' ' ; } cout << endl ; ivec.push_back(7) ; push_heap(ivec.begin(),ivec.end()) ; for(int i = 0 ; i < ivec.size() ; ++i) { cout << ivec[i] << ' ' ; } cout << endl ; pop_heap(ivec.begin(),ivec.end()) ; cout << ivec.back() << endl ; ivec.pop_back() ; for(int i = 0 ; i < ivec.size() ; ++i) { cout << ivec[i] << ' ' ; } cout << endl ; sort_heap(ivec.begin(),ivec.end()) ; for(int i = 0 ; i < ivec.size() ; ++i) { cout << ivec[i] << ' ' ; } cout << endl ; } { //test heap (底层以array 完成) int ia[9] = {0,1,2,3,4,8,9,3,5} ; make_heap(ia,ia+9); sort_heap(ia,ia+9) ; for(int i = 0 ; i < 9 ; ++i) { cout << ia[i] << ' ' ; } cout << endl ; } { //test heap (底层以array完成) int ia[6] = {4,1,7,6,2,5} ; make_heap(ia,ia+6) ; for(int i = 0 ; i < 6 ; ++i) { cout << ia[i] << ' ' ; } cout << endl ; } return 0 ; }
priority_queue:
priority_queue是一个拥有权值观念的queue,它允许加入新元素、移除旧元素、审视元素值等功能。由于这是一个queue,所以只允许在底端加入元素,并从顶端取出元素,除此之外别无其它存取元素的途径。与queue不同,priority_queue缺省情况下是以vector为底层容器,并加上heap处理规则,因此,STL priority_queue往往不被归类为container(容器),而被归类为container adapter。
template<class T, class Sequence = vector<T>, class Compare = less<typename Sequence::value_type> > class priority_queue{ public : typedef typename Sequence::value_type value_type ; typedef typename Sequence::size_type size_type ; typedef typename Sequence::reference reference ; typedef typename Sequence::const_reference const_reference ; protected : Sequence c ; //底层容器 Compare comp ; //元素大小比较标准 public : priority_queue() : c() {} explicit priority_queue(const Compare &x) : c(),comp(x) {} //以下用到的make_heap(),push_heap(),pop_heap()都是泛型算法 //注意,任一个构造函数都立刻于底层容器内产生一个implicit representation heap template<class InputIterator> priority_queue(InputIterator first,InputIterator last, const Compare &x) :c(first,last),comp(x) {make_heap(c.begin(),c.end(),comp) ;} template<class InputIterator> priority_queue(InputIterator first,InputIterator last, const Compare &x) :c(first,last) {make_heap(c.begin(),c.end(),comp) ;} bool empty() const { return c.empty();} size_type size() const {return c.size();} const_reference top() const {return c.front();} void push(const value_type& x){ __STL_TRY{ //push_heap是泛型算法,先利用底层容器的push_back()将新元素 //推入末端,再重排heap。 c.push_back(x) ; push_heap(c.begin(),c.end(),comp) ; //push_heap是泛型算法 } __STL_UNWIND(c.clear()) ; } void pop() { __STL_TRY{ //pop_heap是泛型算法,从heap内取出一个元素。它并不是真正将元素 //弹出,而是重排heap,然后再以底层容器的pop_back()取得被弹出 //的元素。 pop_heap(c.begin(),c.end(),comp) ; c.pop_back() ; } __STL_UNWIND(c.clear()) ; } } ;
priority_queue测试实例:
#include<queue> #include<iostream> #include<algorithm> using namespace std ; int main(void) { //test priority queue... int ia[9] = {0,1,2,3,4,8,9,3,5} ; priority_queue<int> ipq(ia,ia+9) ; cout << "size= " << ipq.size() << endl ; for(int i = 0 ; i < ipq.size() ; ++i) { cout << ipq.top() << ' ' ; } cout << endl ; while(!ipq.empty()) { cout << ipq.top() << ' ' ; ipq.pop() ; } cout << endl ; return 0 ; }
-------------------------------------------------------------------------------------------------------
slist:
STL list是个双向链表。SGI STL另提供一个单向链表slist。slist和list的主要差别在于,前者是迭代器属于单向的Forward Iterator,后者的迭代器属于双向的Bidirectional Iterator。
slist的节点:
//单向链表的节点基本结构 struct __slist_node_base { __slist_node_base* next ; } ; //单向链表的节点结构 template<class T> struct __slist_node : public __slist_node_base { T data ; } ; //全局函数:已知某一节点,插入新节点于其后 inline __slist_node_base* __slist_make_link( __slist_node_base* prev_node, __slist_node_base* new_node) { //令new节点的下一节点为prev节点的下一节点 new_node->next = prev_node->next ; prev_node->next = new_node ; //令prev节点的下一节点指向new节点 return new_node ; } //全局函数:单向链表的大小(元素个数) inline size_t __slist_size(__slist_node_base* node) { size_t result = 0 ; for(; node != 0 ; node = node->next) { ++result ; } return result ; }
slist的迭代器:
//单向链表的迭代器基本结构 struct __slist_iterator_base //没有继承那个标准的iterator,所以要自己定义 { typedef size_t size_type ; typedef ptrdiff_t difference_type ; 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 ; typedef __slist_iterator<T,const T&,const T*> const_iterator ; typedef __slist_iterator<T,Ref,Ptr> self ; typedef T value_type ; typedef Ptr pointer ; typedef Ref reference ; typedef __slist_node<T> list_node ; //定义了operator++(),operator++(int),没有实现operator--,因为是正向迭代器 ..... } ;
slist的数据结构:
template<class T, class Alloc = alloc>
class slist
{
public :
typedef T 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 ;
typedef __slist_iterator<T,T&,T*> iterator ;
typedef __slist_iterator<T,const T&,const T*> const_iterator ;
private :
typedef __slist_node<T> list_node ;
typedef __slist_node_base list_node_base ;
typedef __slist_iterator_base iterator_base ;
typedef simple_alloc<list_node,Alloc> list_node_allocator ;
static list_node* create_node(const value_type& x)
{
list_node* node = list_node_allocator:;allocate() ; //配置空间
__STL_TRY{
construct(&node->data,x) ;
node->next = 0 ;
}
__STL_UNWIND(list_node_allocator:;deallocate(node)) ;
return node ;
}
static void destroy_node(list_node* node)
{
destroy(&node->data) ; //将元素析构
list_node_allocator::deallocate(node) ; //释放空间
}
private :
list_node_base head ; //头部。注意,它不是指针,是实物
public:
slist() {head.next = 0 ;}
~slist(){clear() ;}
public :
iterator begin() {return iterator((list_node*)head.next) ;}
iterator end() {return iteator(0) ;}
iterator size() {const __slist_size(head.next) ;}
bool empty() const {return head.next == 0 ;}
//两个slist互换:只要将head交换互指即可
void swap(slist &L)
{
list_node_base* tmp = head.next;
head.next = L.head.next ;
L.head.next = tmp ;
}
public :
//取头部元素
reference front() {return ((list_node*)head.next)->data ;}
//从头部插入元素(新元素成为slist的第一个元素)
void push_front(const value_type& x)
{
__slist_make_link(&head,create_node(x)) ;
}
//注意,没有push_back()
//从头部取走元素(删除之)。修改head
void pop_front()
{
list_node* node = (list_node*)head.next ;
head.next = node->next ;
destroy_node(node);
}
.....
} ;