序列式容器:
array(build-in)、vector、heap(以算法形式呈现)、priority-queue、list、slist(非标准)、deque、stack(配接器)、queue(配接器)
所谓序列式容器,其中的元素都可序,但是未必有序。
---------------------------------------------------------------------------------------------------
vector:
vector的实现技术,关键在于其对大小的控制以及重新配置时的数据移动效率。
vector的迭代器:
vector的迭代器是普通指针,其型别是Random Access Iterators:
template
class vector{
public :
typedef T value_type ;
typedef value_type* iterator ; ;//vector的迭代器是普通指针
...
} ;
vector采用的数据结构:线性连续空间。它以两个迭代器start和finish分别指向配置得来的连续空间中目前已被使用的范围,并以迭代器end_of_storage指向整块连续空间(含备用空间)的尾端(vector本身就是内含这三个东东):
template
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
struct __list_node{
typedef void *void_pointer ;
void_pointer prev ; //型别为void * 。其实可设为__list_node*
void_pointer next ;
T data ;
}
//由此结构可以看出,这是一个双向链表
由于STL list是一个双向链表(double linked-list),迭代器必须具备前移、后移的能力,所以list提供的是Bidirectional Iterators。
一个重要的性质:插入操作和接合操作都不会造成原有的list迭代器失效。甚至list的元素删除操作也只有“指向被删除元素”的那个迭代器失效,甚至迭代器不受任何影响。
template
struct __list_iterator{
typedef __list_iterator iterator ;
typedef __list_iterator self ;
typedef bidirectional_iterator_tag iterator_category ;
typedef T value_type ;
typedef Ptr pointer ;
typedef Ref reference ;
typedef __list_node* 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 //缺省使用alloc为配置器:w
class list{
protected :
typedef __list_node 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 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
struct __deque_iterator{ //未继承std::iterator
typedef __deque_iterator iterator ;
typedef __deque_iterator 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 deque{
public :
typedef T value_type ;
typedef value_type* pointer ;
typedef size_t size_type ;
public :
typedef __deque_iterator 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 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 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
#include
#include //heap algorithm
using namespace std ;
int main(void)
{
{
//test heap(底层以vector完成)
int ia[9] = {0,1,2,3,4,8,9,3,5};
vector 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 Compare = less >
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
priority_queue(InputIterator first,InputIterator last, const Compare &x)
:c(first,last),comp(x) {make_heap(c.begin(),c.end(),comp) ;}
template
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
#include
#include
using namespace std ;
int main(void)
{
//test priority queue...
int ia[9] = {0,1,2,3,4,8,9,3,5} ;
priority_queue 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
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
struct __slist_iterator : public __slist_iterator_base
{
typedef __slist_iterator iterator ;
typedef __slist_iterator const_iterator ;
typedef __slist_iterator self ;
typedef T value_type ;
typedef Ptr pointer ;
typedef Ref reference ;
typedef __slist_node list_node ;
//定义了operator++(),operator++(int),没有实现operator--,因为是正向迭代器
.....
} ;
slist的数据结构:
template
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 iterator ;
typedef __slist_iterator const_iterator ;
private :
typedef __slist_node list_node ;
typedef __slist_node_base list_node_base ;
typedef __slist_iterator_base iterator_base ;
typedef simple_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);
}
.....
} ;