容器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);
}