目錄
list的邏輯結構
構造函數
拷貝構造函數
賦值運算符重載
返回迭代器的初始位置
返回迭代器的最終位置
?元素的插入
頭插
尾插?
刪除元素
頭刪
尾刪
清空整個鏈表
析構函數
正向迭代器?
反向迭代器
整體代碼?
?上期我們學寫了list的基本操作,本期我們將學習list的模擬實現。
list的邏輯結構
list是一個帶頭結點的雙向循環鏈表。
構造函數
list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}
?這是一個無參的構造函數。
template<class inputiterator>list(inputiterator begin, inputiterator end){_head = new Node;_head->_next = _head;_head->_prev = _head;while (begin != end){push_back(*begin);++begin;}}
這是用迭代器區間進行初始化的構造函數。
list(int n, const T& data = T()){_head = new Node;_head->_next = _head;_head->_prev = _head;while (n != 0){push_back(data);n--;}}
?初始化鏈表的n個節點,使它們的每個結點的數據為data的值。
拷貝構造函數
list(const list<T>& lt){_head = new Node;_head->_next = _head;_head->_prev = _head;list<T> tmp(lt.begin(), lt.end());std::swap(_head, tmp._head);}
這是拷貝構造函數的現代寫法,通過使用迭代器區間構造函數構造了一個list的臨時對象,然后交換了臨時對象和當前對象的頭結點指針,前提是得保證當前結點的頭指針不為空,不然到時候調用析構函數時會將同一空間釋放兩次,導致出錯。
賦值運算符重載
list<T>& operator= (const list<T>lt){std:swap(_head,lt._head);return *this;}
?先通過拷貝構造函數生成了一個臨時對象,然后再交換了臨時對象和當前對象的頭結點指針。
返回迭代器的初始位置
iterator begin(){return iterator(_head->_next);}
迭代器的初始位置就是頭結點的下一位置。
返回迭代器的最終位置
iterator end(){return iterator(_head);}
迭代器的最終位置為最后一個元素的下一位置,即頭結點的位置。
?元素的插入
iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}
?表示在當前位置之前插入一個元素,但是當前位置必須為迭代器類型。
元素的插入會導致迭代器失效嗎?
不會,因為插入元素之后會及時更改迭代器中節點指針的指向。?
頭插
void push_front(const T& x){insert(begin(), x);}
在頭節點之前插入元素,復用了插入函數。?
尾插?
void push_back(const T& x){insert(end(), x);}
在最后一個元素的后面插入元素,復用了插入函數。
刪除元素
iterator erase(iterator pos){//前提是不能刪除掉頭結點assert(pos != end());Node* prev =pos._node->_prev;Node* next = pos._node->_next;delete pos._node;pos._node = nullptr;prev->_next = next;next->_prev = prev;return iterator(next);}
刪除某一位置之后,返回的是當前位置的下一位置的迭代器。?
元素的刪除會導致迭代器失效嗎?
會,因為刪除掉當前位置之后,當前迭代器的節點指針會被釋放。迭代器的節點都被釋放了,所以迭代器自然會失效。
頭刪
void pop_back(){erase(--end());}
尾刪
void pop_back(){erase(--end());}
清空整個鏈表
void clear(){iterator it = begin();while (it != end()){erase(it);it++;}}
刪除除頭結點之外的所有節點。?
析構函數
~list(){clear();delete _head;_head = nullptr;}
析構函數會清空整個鏈表的信息,所以頭結點指針也會被釋放。
正向迭代器?
正向迭代器,其實就是從前往后進行鏈表元素的遍歷。
template<class T,class Ref,class Ptr>struct list_iterator{typedef ListNode<T> Node;typedef list_iterator<T, Ref, Ptr> self;//構造函數list_iterator<T,Ref,Ptr>(Node* node):_node(node){}//解引用操作Ref operator*() {return _node->_data;}//->操作,返回迭代器中的節點指針指向的數據的地址Ptr operator->(){return &_node->_data;}//前置++self& operator++(){_node = _node->_next;return *this;}//后置++self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//前置--self& operator--(){_node = _node->_prev;return *this;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//賦值運算符重載,淺拷貝self& operator=(const self& iterator){_node = iterator._node;return *this;}//判斷是否相等bool operator ==(const self& iterator) const{return _node == iterator._node;}bool operator !=(const self& iterator) const{return _node != iterator._node;}Node* _node;};
list的迭代器和vector的迭代器是不同的,vector的迭代器類型是指針類型,即內置類型。而list的迭代器類型是自定義類型。
反向迭代器
反向迭代器即從后往前進行鏈表元素的遍歷。
namespace yjd {template<class iterator,class Ref,class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> self;//構造函數reverse_iterator(iterator it):_it(it){}//解引用操作Ref operator*(){iterator prev = _it;return *(--prev);}//->Ptr operator->(){iterator prev = _it;return &(--prev);}//++self& operator++(){_it--;return *this;}//--self& operator--(){_it++;return *this;}//判斷是否相等bool operator!=(const self & rit) const{return _it != rit._it;}private:iterator _it;};}
正向迭代器和返現迭代器的開始和結束位置剛好是相對的。反向迭代器的本質仍然是正向迭代器。反向迭代器的++為正向迭代器的--。
雖然說返現迭代器的開始和結束如圖如上圖所示,但是我們真正在訪問的時候并不是訪問所示位置的元素,而是訪問其前一個位置的元素,比如要訪問rbgin位置的元素,則要讓正向迭代器進行--訪問最后一個位置的元素。?
整體代碼?
list.h
#pragma once
#include<assert.h>
#include"reverse_iterator.h"
namespace yjd
{//節點類型template<class T>struct ListNode {ListNode* _next;ListNode* _prev;T _data;ListNode(const T& data=T()):_next(nullptr),_prev(nullptr),_data(data){}};//鏈表的迭代器類型template<class T,class Ref,class Ptr>struct list_iterator{typedef ListNode<T> Node;typedef list_iterator<T, Ref, Ptr> self;//構造函數list_iterator<T,Ref,Ptr>(Node* node):_node(node){}//解引用操作Ref operator*() {return _node->_data;}//->操作,返回迭代器中的節點指針指向的數據的地址Ptr operator->(){return &_node->_data;}//前置++self& operator++(){_node = _node->_next;return *this;}//后置++self operator++(int){self tmp(*this);_node = _node->_next;return tmp;}//前置--self& operator--(){_node = _node->_prev;return *this;}//后置--self operator--(int){self tmp(*this);_node = _node->_prev;return tmp;}//賦值運算符重載,淺拷貝self& operator=(const self& iterator){_node = iterator._node;return *this;}//判斷是否相等bool operator ==(const self& iterator) const{return _node == iterator._node;}bool operator !=(const self& iterator) const{return _node != iterator._node;}Node* _node;};//鏈表類型template<class T>class list {typedef ListNode<T> Node;public:typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;typedef reverse_iterator<iterator, T&, T*> reverse_iterator;typedef reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;//構造函數list(){_head = new Node;_head->_next = _head;_head->_prev = _head;}//構造函數,迭代器區間進行構造template<class inputiterator>list(inputiterator begin, inputiterator end){_head = new Node;_head->_next = _head;_head->_prev = _head;while (begin != end){push_back(*begin);++begin;}}//拷貝構造list(const list<T>& lt){_head = new Node;_head->_next = _head;_head->_prev = _head;list<T> tmp(lt.begin(), lt.end());std::swap(_head, tmp._head);}//賦值運算符重載list<T>& operator= (const list<T>lt){std:swap(_head,lt._head);return *this;}//構造函數n個datalist(int n, const T& data = T()){_head = new Node;_head->_next = _head;_head->_prev = _head;while (n != 0){push_back(data);n--;}}//尾插//void push_back(const T& data)//{// Node* newnode = new Node(data);// Node* tail = _head->_prev;// tail->_next = newnode;// newnode->_prev = tail;// newnode->_next = _head;// _head->_prev = newnode;//}// //rbeginreverse_iterator rbegin(){return reverse_iterator(end());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}//rendreverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}//beginiterator begin(){return iterator(_head->_next);}//enditerator end(){return iterator(_head);}//beginconst_iterator begin() const{return const_iterator(_head->_next);}//endconst_iterator end() const{return const_iterator(_head);}//元素的插入,在pos位置之前插入元素iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;return iterator(newnode);}//list的頭插和尾插,可以復用insert,插入元素之后我們更改的是節點的指針,所以不會涉及到迭代器失效void push_front(const T& x){insert(begin(), x);}void push_back(const T& x){insert(end(), x);}//鏈表刪除某一位置元素,刪除某一位置的元素,迭代器會失效嗎?會失效,因為刪除掉元素之后,會釋放節點的指針,節點的指針都被釋放了,迭代器自然也就沒有意義了iterator erase(iterator pos){//前提是不能刪除掉頭結點assert(pos != end());Node* prev =pos._node->_prev;Node* next = pos._node->_next;delete pos._node;pos._node = nullptr;prev->_next = next;next->_prev = prev;return iterator(next);}//list的尾刪頭刪void pop_back(){erase(--end());}void pop_front(){erase(begin());}//list整個的刪除,這個刪除是刪除所有的有效的節點void clear(){iterator it = begin();while (it != end()){erase(it);it++;}}//析構函數~list(){clear();delete _head;_head = nullptr;}private:Node* _head;};}
reverse_iterator.h
#pragma oncenamespace yjd {template<class iterator,class Ref,class Ptr>class reverse_iterator{public:typedef reverse_iterator<iterator, Ref, Ptr> self;//構造函數reverse_iterator(iterator it):_it(it){}//解引用操作Ref operator*(){iterator prev = _it;return *(--prev);}//->Ptr operator->(){iterator prev = _it;return &(--prev);}//++self& operator++(){_it--;return *this;}//--self& operator--(){_it++;return *this;}//判斷是否相等bool operator!=(const self & rit) const{return _it != rit._it;}private:iterator _it;};}
以上便是list模擬實現的所有知識點。
本期內容到此結束^_^