C++深入学习:STL源码剖析 (3) 从源码深入剖析list
vector和list是最常用的容器,vector是连续的线性空间,list则是非连续的用指针串联起的空间。因此list对于空间的利用是非常充分的,对于任意位置的插入删除操作,list是常数时间完成。
list不仅是双向链表,而且是环形双向链表,因此只需要一个指针便可以实现对整个list的迭代器的标记。为了符合STL的前闭后开的区间要求,list实现时将该指针节点指向一个置于尾端的空白节点,则该节点的前面是尾结点,该节点的后面是头结点,根据此便可以实现诸多函数。
学习list,个人觉得最大的收获是在于熟练运用指针完成相应的变换,list的部分函数也是非常经典的算法题,同时list的设计时也有诸多很巧妙的思想。
STL的list实现由三个模块组成,节点__list_node,
迭代器__list_iterator以及list本身
list 的节点
list是一个双向链表,因此每个节点不仅要存当前的值,还要存储两个指针用来寻找前一个节点和后一个节点。
template<class T>
struct __list_node{
typedef void* void_pointer;
void_pointer prev; //前向指针
void_pointer next; //后向指针
T data; //数据
}
list的迭代器
由于list 不是vector一样节点是连续空间存储,故不能以一个普通指针作为迭代器。list的迭代器必须能够指向list 的节点,同时,还能够实现递增(指向下一个节点)、递减(指向上一个节点)、取值(取出节点的值)、成员取用(取节点的成员对象)等操作。
因此,由于list的迭代器只支持前移后移,而不支持随机访问,因此是 Bidirectional Iterators.
list和vector还有一个比较大的区别,list的插入和接合等都不会造成原有的list迭代器失效,而vector的插入操作当引起空间重配时原有的迭代器全部失效。甚者,list 的删除操作也只有被删除的那个迭代器失效,其他迭代器不受任何影响。
template<class T,class Ref,class Ptr>
struct __list_iterator{
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的节点
//构造函数
__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;
}
//对迭代器取值返回节点存储的数据值
reference operator*()const{
return (*node).data;}
//重载->,返回引用
pointer operator->()const {
return &(operator*());}
//重载递增递减
self& operator++()
{
node=(link_type)((*node).next);
return *this;
}
self operator++(int) //后+,返回原值的拷贝,然后原值++
{
self tmp=*this;
++(*this);
return tmp;
}
self& operator--()
{
node=(link_type)((*node).prev);
return *this;
}
self operator--(int) //后-,返回原值的拷贝,然后原值--
{
self tmp=*this;
--(*this);
return tmp;
}
}
list的数据结构
list是一个环形双向链表,因此只需要一个指针就可以完成整个链表的遍历。
template<class T,class Alloc=alloc>
class list{
protected:
typedef __list_node<T> list_node;
public:
typedef list_node* link_type