容器list双向环形链表
list相较于vector的连续线性空间,list显得复杂许多,好处是每次插入或者删除一个元素,就配置或释放一个元素空间。因此list对于空间的运用绝对精准,一点也不浪费。而且对于任何位置的元素插入和元素移除,list永远是常数时间<STL源码剖析>
相关图
节点设计
template <class T>
struct __list_node {
typedef void* void_pointer;//缺陷,使用时都得强转成_list_node类型
void_pointer next; // 指针指向下一个节点
void_pointer prev; // 指针指向前一个节点
T data; //相应数据类型的data
};
__list_iterator
template<class T, class Ref, class Ptr>
struct __list_iterator {
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
typedef __list_iterator<T, Ref, Ptr> self;
// 定义五种必要的迭代器类型
typedef bidirectional_iterator_tag iterator_category; // (1) 双向
typedef T value_type; // (2) 数据类型
typedef Ptr pointer; // (3) 指针类型
typedef Ref reference; // (4) 引用
typedef __list_node<T>* link_type; //节点指针类型
typedef size_t size_type;
typedef ptrdiff_t difference_type; // (5)
link_type node; // 指向容器的节点
// 对迭代器解引用,获取节点里保存的data数据
reference operator*() const { return (*node).data; }
//重载前++运算符
self& operator++() {
//将当前节点指向下一个节点
node = (link_type)((*node).next);
return *this;
}
//重载后++运算符
self operator++(int) {
self tmp = *this;
//当前节点++
++*this;
//将当前节点的赋给临时变量并返回
return tmp;
}
// 重载前--运算符
self& operator--() {
//将指针指向当前节点的上一个节点(pre)
node = (link_type)((*node).prev);
return *this;
}
//重载后--
self operator--(int) {
self tmp = *this;
//前--
--*this;
//返回当前节点的临时值
return tmp;
}
};
list容器
template <class T, class Alloc = alloc> //默认alloc
class list {
protected:
typedef void* void_pointer;
//节点
typedef __list_node<T> list_node;
//空间配置器,每次分配一个list_node节点大小的内存
typedef simple_alloc<list_node, Alloc> list_node_allocator;
public:
typedef T value_type; //元素类型
typedef value_type* pointer; //指针类型
typedef const value_type* const_pointer; //const指针类型
typedef value_type& reference; //引用
typedef const value_type& const_reference;
typedef list_node* link_type; //节点类型
typedef size_t size_type;
typedef ptrdiff_t difference_type;
public:
//迭代器
//迭代器使用者,使用迭代器时,便会将迭代器中的节点指针指向容器
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;
protected:
// 空间配置申请一个节点内存的大小,并返回指针
link_type get_node() { return list_node_allocator::allocate(); }
// 释放内存
void put_node(link_type p) { list_node_allocator::deallocate(p); }
// 申请一个节点,并进行相应的赋值。insert时调用
link_type create_node(const T& x) {
link_type p = get_node();
construct(&p->data, x); //很重要调用place new构造函数;
return p;
}
// 释放一个节点 erase时调用
void destroy_node(link_type p) {
//很重要调用析构函数
//inline void destroy(T* pointer) {
//pointer->~T(); // 喚起 dtor ~T()
destroy(&p->data);
put_node(p); // 释放内存给内存池
}
protected:
//双向环形链表,永远指向最后一个节点的下一个节点,以及第一个节点的上一个节点
//空节点,哨兵节点
//容器内唯一一个元素
link_type node;
//容器的节点的初始化
list() { empty_initialize(); }
void empty_initialize() {
node = get_node(); // 申请一个节点
node->next = node; // 令头尾都指向自己不设元素值
node->prev = node;
}
//容器内保存的节点的下一个节点是第一个节点
iterator begin() { return (link_type)((*node).next); }
//end是尾节点的下一个节点,也就是容器内保存的节点
iterator end() { return node; }
//常见函数实现
// 往头结点前插入一个元素
void push_front(const T& x) { insert(begin(), x); }
// 往尾节点插入一个元素,end()之前也就是最后一个节点的后面
void push_back(const T& x) { insert(end(), x); }
// 移除头结点
void pop_front() { erase(begin()); }
// 移除尾节点,容器节点是尾节点的下一个,所以移除(--tmp)
void pop_back() {
iterator tmp = end();
erase(--tmp);
}
//向位置position之前插入一个元素
iterator insert(iterator position, const T& x) {
link_type tmp = create_node(x); // 申请一个节点,data数据为x
// 调整节点内的两个指针(总共需要调整四个指针的指向,position的prev tmp的prev和next,position.prev的next)
//tmp的next指向position
tmp->next = position.node;
//tmp的pre指针指向position的prev;
tmp->prev = position.node->prev;
//position上个节点的next指针指向tmp
(link_type(position.node->prev))->next = tmp;
//position的prev执行tmp
position.node->prev = tmp;
return tmp;
}
// 移除迭代器 position 所指节点
iterator erase(iterator position) {
link_type next_node = link_type(position.node->next);
link_type prev_node = link_type(position.node->prev);
//移除需要调整两个指针(position上一个节点的next,以及position下一个节点的pre)
prev_node->next = next_node;
next_node->prev = prev_node;
//移除节点
destroy_node(position.node);
//用移除了的节点的下一个节点初始化一个迭代器并返回
return iterator(next_node);
}