序列式容器( List)
前言
在SGI STL中,list容器是一个循环的双向链表,它的内存空间效率较前文介绍的vector容器高。相对于 vector 的连续线性空间,list 就显得复杂许多,他的好处是每次插入和删除一个元素,就配置和释放一个元素空间。因此,list 对于空间的运用有绝对的精准,一点也不浪费。而且,对于任何位置的元素插入或元素移除,list 永远是常数时间。
与vector容器不同的是,list容器在进行插入操作或拼接操作时,迭代器并不会失效;且不能以普通指针作为迭代器,因为普通指针的+或-操作只能指向连续空间的后移地址或前移个地址,不能保证指向list的下一个节点,迭代器必须是双向迭代器,因为list容器具备有前移和后移的能力。
list 和 vector 是两个最常被使用的容器 , 什么时机最适合使用哪种容器 , 必须视元素的多寡,元素的构造 复杂度 ( 有无 non-trivial copy constructor, non-tirivial copy assigiunen operator) 、元素存取行为的特性而定。
list节点和list数据结构
在list容器中,list本身和list节点是分开设计的,list节点结构是存储数据和指向相邻节点的指针;如下源码所示:
template<class T>
struct _list_node
{
typedef _list_node* void_pointer;
void_pointer prev; //指向直接前驱节点
void_pointer next; //指向直接后继节点
T data; //节点存储的数据
};
list本身的数据结构是只有一个指向链表节点的指针,因为list容器是循环双向链表,则足够遍历整个链表;如下源码所示:
//以下是双向链表list类的定义,分配器_Alloc默认为第二级配置器
template <class T, class Alloc = alloc>
class list
{
protected:
typedef _list_node<T> list_node;
public:
typedef list_node* link_type;
protected:
link_type node; //list是一个环状双向链表,一个指针即可表示整个环状双向链表,指向尾端的空白节点
...
};
下面给出list节点和list本身的数据结构图:
list容器的迭代器
list容器的内存空间存储不一定是连续的,则不能用普通指针做为迭代器;list的迭代器要能够指向list的节点,并且可以进行正确的递增、递减、取值和成员存取等操作。所以迭代器要有前移、后退的能力,所以list的迭代器为双向迭代器。
这也是导致list容器的排序成员函数sort()不能使用STL算法中的排序函数,因为STL中的排序算法接受的迭代器是随机访问迭代器;
list容器在进行插入和拼接操作时迭代器不会失效;以下是源码对迭代器的定义:
#include "listNode.h"
#include "iterator.h"
namespace EasySTL
{
//迭代器本身不是指针,因为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; //指向List节点的迭代器
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; //普通指针指向节点
//constructor
_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 {