目錄
list
結點類
結點類的構造函數
list的尾插尾刪
list的頭插頭刪
迭代器
++運算符重載
--運算符重載
==和!= 運算符重載
* 和 -> 運算符重載
?list 的insert
list的erase
list
list實際上是一個帶頭雙向循環鏈表,要實現list,則首先需要實現一個結點類,而一個結點需要存儲的信息為:數據、前驅指針、后繼指針
而對于該結點類的成員函數來說,我們只需實現一個構造函數即可,因為該結點類只需要根據數據來構造一個結點即可,而結點的釋放則由list的析構函數來完成,
結點類
結點類的基本結構:
template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _date;ListNode(const T& pos = T()){_next = nullptr;_prev = nullptr;_date = pos;}};
這里用struct 的原因是因為ListNode 的?每個成員變量都會被頻繁調用。
用struct則不需要封裝了。
結點類的構造函數
構造函數直接根據所給數據構造一個結點即可,構造出來的結點的數據域存儲的就是所給數據,而前驅指針和后繼指針均初始化為空指針即可
ListNode(const T& pos = T()){_next = nullptr;_prev = nullptr;_date = pos;}
list的尾插尾刪
template<class T>class list{public:typedef ListNode<T> node; list():_head(new node){_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){node* head = _head;node* tail = _head->_prev;node* p = new node(x);tail->_next = p;p->_prev = tail;p->_next = head;head->_prev = p;}void pop_back(){assert(_head != _head->_next);node* head = _head;node* tail = head->_prev;node* newtail = tail->_prev;newtail->_next = head;head->_prev = newtail;delete[] tail;}private:node* _head;};
list的頭插頭刪
template<class T>class list{public: typedef ListNode<T> node;list():_head(new node){_head->_next = _head;_head->_prev = _head;}void push_front(const T& x){node* newnode = new node(x);node* head = _head;node* tail = _head->_next;head->_next = newnode;newnode->_prev = head;newnode->_next = tail;tail->_prev = newnode;}void pop_front(){assert(_head != _head->_next);node* head = _head;node* tail = _head->_next;head->_next = tail->_next;tail->_next->_prev = head;delete[]tail;}private:node* _head;};
迭代器
迭代器有兩種實現方式,具體應根據容器底層數據結構實現:
- 原生態指針,比如:vector和string ->物理空間是連續的,因為string和vector對象都將其數據存儲在了一塊連續的內存空間,我們通過指針進行自增、自減以及解引用等操作,就可以對相應位置的數據進行一系列操作,因此string和vector當中的迭代器就是原生指針。
- .將原生態指針進行封裝,因迭代器使用形式與指針完全相同,因此在自定義的類中必須實現以下方法:
指針可以解引用,迭代器的類中必須重載operator*()
指針可以通過->訪問其所指空間成員,迭代器類中必須重載oprator->()
指針可以++向后移動,迭代器類中必須重載operator++()與operator++(int)
至于operator--()/operator--(int) 是否需要重載,根據具體的結構來抉擇,雙向鏈表可
以向前 移動,所以需要重載,如果是forward_list就不需要重載–
迭代器需要進行是否相等的比較,因此還需要重載operator==()與operator!=()
但是對于list來說,其各個結點在內存當中的位置是隨機的,并不是連續的,我們不能僅通過結點指針的自增、自減以及解引用等操作對相應結點的數據進行操作,
總結:list的迭代器 實際上就是對結點指針進行了封裝,對其各種運算符進行了重載,使得結點指針的各種行為看起來和普通指針一樣,(例如,對結點指針自增就能指向下一個結點 p = p->next)
template<class T1, class T2>struct Reverse_iterator{typedef Reverse_iterator<T1, T2> self;typedef ListNode<T1> node;node* _it;Reverse_iterator(node* pos);self& operator++();self operator++(int);self& operator--();self operator--(int);T2& operator*();T2* operator->();bool operator!=(const self& pos);bool operator==(const self& pos);};
迭代器模板參數說明:
構造函數
迭代器類實際上就是對結點指針進行了封裝
其成員變量就是結點指針,所以其構造函數直接根據所給結點指針構造一個迭代器對象即可,
Reverse_iterator(node* pos){_it = pos;}
拷貝構造,operator,析構函數我們都不需要寫,因為成員變量是內置類型(指針), 用編譯器默認生成的就可以。
++運算符重載
self& operator++()//前置{_it =_it->_prev;return *this;}self operator++(int)//后置{self tmp(_it);_it = _it->_prev;return tmp;}
前置++原本的作用是將數據自增,然后返回自增后的數據,
而對于結點迭代器的前置++:應該先讓結點指針指向后一個結點.然后再返回“自增”后的結點迭代器即可
后置++,先拷貝構造當前迭代器結點, 然后讓當前迭代器結點的指針自增指向下一個結點,最后返回“自增”前的結點迭代器即可,
--運算符重載
self& operator--()//前置{_it = _it->_next;return *this;}self operator--(int)//后置{self tmp(_it);_it = _it->_next;return tmp;}
前置- -:當前迭代器結點中的指針指向前一個結點,然后再返回“自減”后的結點迭代器即可,
后置--:拷貝構造當前迭代器對象 -> 當前迭代器結點中的指針自減指向前一個結點 ->返回自減前的迭代器。
==和!= 運算符重載
bool operator!=(const self& pos){return _it != pos._it;}bool operator==(const self& pos){return _it == pos._it;}
這里注意形參別寫錯就好了。
* 和 -> 運算符重載
使用解引用操作符時,是想得到該指針指向的數據內容
因此,我們直接返回當前結點指針所指結點的數據即可,這里需要使用引用返回,因為解引用后可能需要對數據進行修改,
T2& operator*(){return _it->_date;}
->返回當前迭代器結點的指針所指結點的數據的地址
T2* operator->(){return &_it->_date;}
使用場景:
?list 的insert
insert函數可以在所給迭代器pos之前插入一個新結點,
1.先根據所給迭代器pos得到該位置處的結點指針
2.然后通過該指針找到前一個位置的結點指針last
根據所給數據x構造一個新結點
iterator insert(iterator pos,const T& x){node* newnode = new node(x);node* next = pos._node;node* last = next->_prev;last->_next = newnode;newnode->_prev = last;newnode->_next = next;next->_prev = newnode;return iterator(newnode);}
list的erase
erase函數可以刪除所給迭代器位置的結點,
注意**:pos不可以是哨兵位的迭代器,即不能刪除哨兵位 pos迭代器結點中的指針不能為空**
1.根據所給迭代器得到該位置處的結點指針self
2.通過self指針找到前一個位置的結點指針last,以及后一個位置的結點指針next
3.緊接著釋放cur結點,最后prev和next結點進行鏈接
iterator erase(iterator pos){assert(pos._node);assert(_head != _head->_next);node* self = pos._node;node* next = self->_next;node* last = self->_prev;last->_next = next;next->_prev = last;delete[] self;return iterator(next);}
關于insert 和 erase 迭代器失效的問題:
insert不會導致迭代器失效,因為pos迭代器中的節點指針仍然指向原來的節點。
問:erase之后, pos迭代器是否失效:
一定失效,因為此時pos迭代器中的節點指針指向的節點已經被釋放了,該指針相當于是野指針。
?最后所有代碼如下:
?
namespace bit
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _date;ListNode(const T& pos = T()){_next = nullptr;_prev = nullptr;_date = pos;}};template<class T1,class T2 = T1>struct ListIterator{typedef ListIterator<T1,T2> iterator;typedef ListNode<T1> node;node* _node;ListIterator(node* pos){_node = pos;}T2& operator*(){return _node->_date;}iterator& operator++(){_node = _node->_next;return *this;} iterator operator++(int){iterator tmp(_node);_node = _node->_next;return tmp;}iterator& operator--(){_node = _node->_prev;return *this;}iterator operator--(int){iterator tmp(_node);_node = _node->_prev;return tmp;}T2* operator->(){return &_node->_date;}bool operator!=(const iterator& pos){return _node != pos._node;}bool operator==(const iterator& pos){return _node == pos._node;}};template<class T1, class T2>struct Reverse_iterator{typedef Reverse_iterator<T1, T2> self;typedef ListNode<T1> node;node* _it;Reverse_iterator(node* pos){_it = pos;}self& operator++(){_it =_it->_prev;return *this;}self operator++(int){self tmp(_it);_it = _it->_prev;return tmp;}self& operator--(){_it = _it->_next;return *this;}self operator--(int){self tmp(_it);_it = _it->_next;return tmp;}T2& operator*(){return _it->_date;}T2* operator->(){return &_it->_date;}bool operator!=(const self& pos){return _it != pos._it;}bool operator==(const self& pos){return _it == pos._it;}};template<class T>class list{public:typedef Reverse_iterator<T, T> reverse_iterator;typedef Reverse_iterator<T, const T> const_reverse_iterator;typedef ListNode<T> node;typedef ListIterator<T> iterator;typedef ListIterator<T,const T> const_iterator;list():_head(new node){_head->_next = _head;_head->_prev = _head;}list(const list& pos){_head = new node;_head->_next = _head;_head->_prev = _head;for (auto e : pos){push_back(e);}}list(initializer_list<T> il){_head = new node;_head->_next = _head;_head->_prev = _head;for (auto e : il){push_back(e);}}void push_back(const T& x){node* head = _head;node* tail = _head->_prev;node* p = new node(x);tail->_next = p;p->_prev = tail;p->_next = head;head->_prev = p;}void pop_back(){assert(_head != _head->_next);node* head = _head;node* tail = head->_prev;node* newtail = tail->_prev;newtail->_next = head;head->_prev = newtail;delete[] tail;}reverse_iterator rbegin(){return reverse_iterator(_head->_prev);}reverse_iterator rend(){return reverse_iterator(_head);}const_reverse_iterator crbegin()const{return const_reverse_iterator(_head->_prev);}const_reverse_iterator crend()const{return const_reverse_iterator(_head);}iterator begin(){return iterator(_head->_next);}const_iterator begin()const{return const_iterator(_head->_next);}iterator end(){return iterator(_head);}const_iterator end()const{return const_iterator(_head);}void push_front(const T& x){node* newnode = new node(x);node* head = _head;node* tail = _head->_next;head->_next = newnode;newnode->_prev = head;newnode->_next = tail;tail->_prev = newnode;}void pop_front(){assert(_head != _head->_next);node* head = _head;node* tail = _head->_next;head->_next = tail->_next;tail->_next->_prev = head;delete[]tail;}iterator insert(iterator pos,const T& x){node* newnode = new node(x);node* next = pos._node;node* last = next->_prev;last->_next = newnode;newnode->_prev = last;newnode->_next = next;next->_prev = newnode;return iterator(newnode);}iterator erase(iterator pos){assert(pos._node);assert(_head != _head->_next);node* self = pos._node;node* next = self->_next;node* last = self->_prev;last->_next = next;next->_prev = last;delete[] self;return iterator(next);}~list(){iterator it1 = begin();while (it1 != end()){it1 = erase(it1);}delete _head;_head = nullptr;}private:node* _head;};