二級域名子域名大全領(lǐng)碩網(wǎng)站seo優(yōu)化
父母就像迭代器,封裝了他們的脆弱......?
手撕list目錄:
一、list的常用接口及其使用
1.1list 構(gòu)造函數(shù)與增刪查改
1.2list 特殊接口
1.3list 排序性能分析
二、list 迭代器實(shí)現(xiàn)(重點(diǎn)+難點(diǎn))
關(guān)于迭代器的引入知識:
2.1迭代器的分類
2.2?list 迭代器失效問題(和vector有差異)
2.3list 迭代器源碼模板
2.4list 整體基本框架
三、手撕list迭代器
3.1重載operator*()
3.2重載++、–、!=
3.3 利用類模板優(yōu)化
四、增刪查改
4.1 insert(參數(shù)必須加引用,擔(dān)心非內(nèi)置類型)和erase
4.2 push_back和push_front
4.3??pop_back和pop_front
五、list 構(gòu)造+賦值重載
5.1默認(rèn)構(gòu)造+迭代器區(qū)間構(gòu)造+拷貝構(gòu)造
5.2 賦值重載現(xiàn)代寫法
5.3 類名和類型的問題(C++的一個坑)
六、list和vector的對比(重點(diǎn))
七、源碼合集
一、list的常用接口及其使用
1.1list 構(gòu)造函數(shù)與增刪查改
list 是可以在常數(shù)范圍內(nèi)在任意位置進(jìn)行插入和刪除的序列式容器,其底層是帶頭雙向循環(huán)鏈表;list 常用接口的使用和 string、vector 系列容器的接口使用一樣,這里我不詳細(xì)介紹,請看我們的老朋友:cplusplus.com - The C++ Resources Network
構(gòu)造函數(shù):
構(gòu)造函數(shù)(constructor) | 接口說明 |
list (size_type n, const value_type& val = value_type()) | 構(gòu)造的 list 中包含n個值為val的元素 |
list() | 構(gòu)造空的 list |
構(gòu)造空的 list | 拷貝構(gòu)造函數(shù) |
list (InputIterator first, InputIterator last) | 用 [first, last) 區(qū)間中的元素構(gòu)造 list |
增刪查改:?
函數(shù)說明 | 接口說明 |
push_front | 在list首元素前插入值為val的元素 |
pop_front | 刪除list中第一個元素 |
push_back | 在list尾部插入值為val的元素 |
pop_back | 刪除list中最后一個元素 |
insert | 在list position 位置中插入值為val的元素 |
erase | 刪除list position位置的元素 |
swap | 交換兩個list中的元素 |
clear | 清空list中的有效元素 |
注意:
1、由于 list 的物理結(jié)構(gòu)是非連續(xù)的 – 前一個節(jié)點(diǎn)地址和后一個節(jié)點(diǎn)地址的位置關(guān)系是隨機(jī)的,所以 list 不支持隨機(jī)訪問,自然也就不支持 [ ] 操作;
2、list 不支持reserve操作,因?yàn)?list 的節(jié)點(diǎn)是使用時開辟,使用完銷毀,不能預(yù)留空間;
(從這個特點(diǎn)也容易看出來,如果需要一直插入刪除元素,利用list更好)
1.2list 特殊接口
除了上述?STL 容器基本都有的一般接口外,list 還提供一些獨(dú)有的特殊操作接口,如下:
函數(shù)聲明 | 接口說明 |
---|---|
splice | 將 list1 中的元素轉(zhuǎn)移到 list2 中 |
remove | 移除 list 中的指定元素 |
unique | 鏈表去重(sort之后才可以用) |
merge | 合并兩個鏈表 |
sort | 鏈表排序(探究為什么list自己寫sort) |
reverse | 鏈表逆置 |
題外話: 為什么list需要自己實(shí)現(xiàn)sort接口??難道說庫中的封裝性不好?效率不高?
?我們先使用庫中自己的sort函數(shù):
我們使用算法庫中的sort函數(shù):
void test_sort()
{list<int> l1{ 5,6,4,8,9,2,7 };//C++ 11寫法sort(l1.begin(),l1.end());for(auto l : l1){cout << l << " ";}cout << endl;
}
報錯了(意外之中,如果不報錯我還寫這個知識點(diǎn)干啥 doge) 報錯原因說沒有-迭代器
讓我們看看sort源碼~
這一切的一切都是因?yàn)閟ort的迭代器引起的!!?
注意:
1、鏈表排序只能使用 list 提供的 sort 接口,而不能使用 algorithm 提供的 sort 接口,因?yàn)殒湵?span style="color:#fe2c24;">物理地址不連續(xù),迭代器為雙向迭代器,不支持 + - 操作,而算法庫中的 sort 函數(shù)需要支持 + - 的隨機(jī)迭代器;
2、鏈表去重之前必須保證鏈表有序,否則去重不完全;
3、兩個有序鏈表合并之后仍然保存有序;
?最后,雖然 list 提供了這些具有特殊功能的接口,它們也確實(shí)有一定的作用,但是實(shí)際上這些特殊接口使用頻率非常低,包括 sort 接口 (鏈表排序的效率太低)。
1.3list 排序性能分析
雖然鏈表排序只能使用 list 提供的 sort 接口,而不能使用 algorithm 提供的 sort 接口,但是其使用頻率仍然非常低,這是由于鏈表排序的效率太低了,我們可以通過對比兩組測試數(shù)據(jù)來直觀的感受鏈表排序的效率。
測試一:vector 排序與 list 排序性能對比
//vector sort 和 list sort 性能對比 -- release 版本下
void test_op1() {srand((size_t)time(0));const int N = 1000000; //100萬個數(shù)據(jù)vector<int> v;v.reserve(N);list<int> lt;for (int i = 0; i < N; ++i){auto e = rand();v.push_back(e);lt.push_back(e);}//vector sortint begin1 = clock();sort(v.begin(), v.end());int end1 = clock();//list sortint begin2 = clock();lt.sort();int end2 = clock();printf("vector sort:%d\n", end1 - begin1);printf("list sort:%d\n", end2 - begin2);
}
測試二:list 直接進(jìn)行排序與將數(shù)據(jù)拷貝到 vector 中使用 vector 排序后再將數(shù)據(jù)拷回 list 中性能對比
//list sort 與 將數(shù)據(jù)轉(zhuǎn)移到 vector 中進(jìn)行排序后拷貝回來性能對比 -- release 版本下
void test_op2()
{srand(time(0));const int N = 1000000; //100萬個數(shù)據(jù)list<int> lt1;list<int> lt2;for (int i = 0; i < N; ++i){auto e = rand();lt1.push_back(e);lt2.push_back(e);}//list sort -- lt1int begin1 = clock();lt1.sort();int end1 = clock();// 將數(shù)據(jù)拷貝到vector中排序,排完以后再拷貝回來 -- lt2int begin2 = clock();vector<int> v;v.reserve(N);for (auto e : lt2) //拷貝{v.push_back(e);}sort(v.begin(), v.end()); //排序lt2.assign(v.begin(), v.end()); //拷貝int end2 = clock();printf("list1 sort:%d\n", end1 - begin1);printf("list2 sort:%d\n", end2 - begin2);
}
?可以看到,list sort 的效率遠(yuǎn)低于 vector sort,甚至于說,直接使用 list sort 的效率都不如先將數(shù)據(jù)拷貝到 vector 中,然后使用 vector sort,排序之后再將數(shù)據(jù)拷貝回 list 中快;所以list中的sort接口是很挫的!!
二、list 迭代器實(shí)現(xiàn)(重點(diǎn)+難點(diǎn))
關(guān)于迭代器的引入知識:
迭代器的價值在于封裝底層的實(shí)現(xiàn),不具體暴露底層的實(shí)現(xiàn)細(xì)節(jié),提供統(tǒng)一的訪問方式
iterator只是代言人!!真正的牛逼大佬其實(shí)是_list_iterator
為什么在 list 中將迭代器搞成指針這招不好用了呢??
在數(shù)組中,*指針就是元素,指針++就是 +sizeof(T) 對象大小,沒辦法,誰叫他們物理空間連續(xù),結(jié)構(gòu)NB,所以對于vector和string類而言,物理空間是連續(xù)的,原生的指針就是迭代器了,解引用就是數(shù)據(jù)了。但是對于這里的list而言,空間是不連續(xù)的
解決方法:
此時如果解引用是拿不到數(shù)據(jù)的(空間不連續(xù)),更不用說++指向下一個結(jié)點(diǎn)了。所以,對于list的迭代器,原生指針已經(jīng)不符合我們的需求了,我們需要去進(jìn)行特殊處理:進(jìn)行類的封裝。我們可以通過類的封裝以及運(yùn)算符重載支持,這樣就可以實(shí)現(xiàn)像內(nèi)置類型一樣的運(yùn)算符
迭代器的倆個特征:
1.解引用2.++ / --
運(yùn)算符重載的大任務(wù):
實(shí)現(xiàn)解引用operator*()和++函數(shù)
2.1迭代器的分類
按照迭代器的功能,迭代器一共可以分為以下三類:
- 單向迭代器 – 迭代器僅僅支持 ++ 和解引用操作(單鏈表,哈希)
- 雙向迭代器 – 迭代器支持 ++、-- 和解引用操作,但不支持 +、- 操作(list 雙向鏈表)
- 隨機(jī)迭代器 – 迭代器不僅支持 ++、-- 和解引用操作,還支持 +、- 操作,即迭代器能夠隨機(jī)訪問(string,vector)
這也充分說明,vector和string是可以用庫中的sort函數(shù)的
?
迭代器還可以分成普通迭代器和const迭代器倆類:
//1.const T* p1
list<int>::const_iterator cit = lt.begin();
//2.T* const p2
const list<int>::iterator cit = lt.begin();
//不符合const迭代器的行為,因?yàn)楸Wo(hù)迭代器本身不能修改,那么我們也就不能++迭代器
靈魂拷問:const迭代器是p1還是p2?p1
const迭代器類似p1的行為,保護(hù)指向的對象不被修改,迭代器本身可以修改
2.2?list 迭代器失效問題(和vector有差異)
vector迭代器失效:insert擴(kuò)容+erase的時候會失效
和 vector 不同,list 進(jìn)行 insert 操作后并不會產(chǎn)生迭代器失效問題,因?yàn)?list 插入的新節(jié)點(diǎn)是動態(tài)開辟的,同時由于 list 每個節(jié)點(diǎn)的物理地址是不相關(guān)的,所以插入的新節(jié)點(diǎn)并不會影響原來其他節(jié)點(diǎn)的地址;
但是 list erase 之后會發(fā)生迭代器失效,因?yàn)?list 刪除節(jié)點(diǎn)會直接將該節(jié)點(diǎn)釋放掉,此時我們再訪問該節(jié)點(diǎn)就會造成越界訪問
2.3list 迭代器源碼模板
我們知道,迭代器是類似于指針一樣的東西,即迭代器要能夠?qū)崿F(xiàn)指針相關(guān)的全部或部分操作 – ++、–、*、+、-;對我們之前 string 和 vector 的迭代器來說,迭代器就是原生指針,所以它天然的就支持上述操作;
但是對于 list 來說,list 的節(jié)點(diǎn)是一個結(jié)構(gòu)體,同時 list 每個節(jié)點(diǎn)的物理地址是不連續(xù)的,如果此時我們還簡單將節(jié)點(diǎn)的指針 typedef 為迭代器的話,那么顯然它是不能夠?qū)崿F(xiàn)解引用、++ 等操作的,所以我們需要用結(jié)構(gòu)體/類來對迭代器進(jìn)行封裝,再配合運(yùn)算符重載等操作讓迭代器能夠?qū)崿F(xiàn)解引用、++、-- 等操作
框架代碼如下:
//節(jié)點(diǎn)定義
template <class T>
struct __list_node {typedef void* void_pointer;void_pointer next;void_pointer prev;T data;
};//迭代器定義
typedef __list_iterator<T, T&, T*> iterator;
typedef __list_iterator<T, const T&, const T*> const_iterator;//迭代器類
template<class T, class Ref, class Ptr>
struct __list_iterator {typedef __list_iterator<T, Ref, Ptr> self;typedef __list_node<T>* link_type; //節(jié)點(diǎn)的指針link_type node; //類成員變量__list_iterator(link_type x) : node(x) {} //將節(jié)點(diǎn)指針構(gòu)造為類對象//... 使用運(yùn)算符重載支持迭代器的各種行為self& operator++() {...}self& operator--() {...}Ref operator*() const {...}
};
2.4list 整體基本框架
namespace lzy
{//結(jié)點(diǎn)template<class T>struct list_node{list_node* _next;list_node* _prev;T _data;list_node(const T& x)//節(jié)點(diǎn)的構(gòu)造函數(shù)及初始化列表:_next(nullptr), _prev(nullptr), _data(x){}};template<class T>class list{typedef list_node<T> node;public://迭代器typedef __list_iterator<T> iterator;typedef __list_const_iterator<T> const_iterator;//構(gòu)造list(){_head = new node(T());_head->_next = _head;_head->_prev = _head;}private:node* _head;size_t _size;};
}
三、手撕list迭代器
迭代器的實(shí)現(xiàn)我們需要去考慮普通迭代器和const迭代器。這兩種迭代器的不同,也會帶來不同的接口。我們可以分別單獨(dú)去進(jìn)行實(shí)現(xiàn),我們先來看一看簡單的構(gòu)造迭代器,只需要提供一個結(jié)點(diǎn)即可,看一看實(shí)現(xiàn)的基本框架:
template<class T>struct __list_iterator{typedef list_node<T> node;node* _pnode;__list_iterator(node* p):_pnode(p){}}
為什么迭代器不寫拷貝構(gòu)造函數(shù)?淺拷貝真的可以嗎?
對于迭代器的拷貝構(gòu)造和賦值重載我們并不需要自己去手動實(shí)現(xiàn),編譯器默認(rèn)生成的就是淺拷貝,而我們需要的就是淺拷貝,這也說明了,并不是說如果有指針就需要我們?nèi)?shí)現(xiàn)深拷貝,而且迭代器不需要寫析構(gòu)函數(shù),所以說不需要深拷貝
為什么聊這個問題?因?yàn)閘ist<int>::iterator it=v.begin() 這就是一個拷貝構(gòu)造
3.1重載operator*()
這個比較簡單,就是要獲取迭代器指向的數(shù)據(jù),并且返回數(shù)據(jù)的引用:
T& operator*()
{return _pnode->_data;
}
?3.2重載++、–、!=
__list_iterator<T>& operator++(){_pnode = _pnode->_next;return *this;}__list_iterator<T>& operator--(){_pnode = _pnode->_prev;return *this;}bool operator!=(const __list_iterator<T>& it){return _pnode != it._pnode;}
?如果按照上面的做法,我們在來看看此時普通迭代器和const迭代器的區(qū)別:
//typedef __list_iterator<T> iterator;
//typedef __list_const_iterator<T> const_iterator;template<class T>struct __list_iterator{typedef list_node<T> node;node* _pnode;__list_iterator(node* p):_pnode(p){}T& operator*(){return _pnode->_data;}__list_iterator<T>& operator++(){_pnode = _pnode->_next;return *this;}__list_iterator<T>& operator--(){_pnode = _pnode->_prev;return *this;}bool operator!=(const __list_iterator<T>& it){return _pnode != it._pnode;}};//跟普通迭代器的區(qū)別:遍歷,不能用*it修改數(shù)據(jù)template<class T>struct __list_const_iterator{typedef list_node<T> node;node* _pnode;__list_const_iterator(node* p):_pnode(p){}const T& operator*(){return _pnode->_data;}__list_const_iterator<T>& operator++(){_pnode = _pnode->_next;return *this;}__list_const_iterator<T>& operator--(){_pnode = _pnode->_prev;return *this;}bool operator!=(const __list_const_iterator<T>& it){return _pnode != it._pnode;}};
代碼冗余!!!代碼冗余!!!代碼冗余!!!
如果是這樣子去實(shí)現(xiàn)的話,我們就會發(fā)現(xiàn),這兩個迭代器的實(shí)現(xiàn)并沒有多大的區(qū)別,唯一的區(qū)別就在于operator*的不同。const迭代器和普通迭代器的唯一區(qū)別就是普通迭代器返回T&,可讀可寫,const迭代器返回const T&,可讀不可寫。我們可以參考源碼的實(shí)現(xiàn):類模板參數(shù)解決這個問題,這也是迭代器的強(qiáng)大之處
3.3 利用類模板優(yōu)化
template <class T,class Ref,class Ptr>
//typedef __list_iterator<T, T&, T*> iterator;
//typedef __list_iterator<T, const T&, const T*> const_iterator;
利用類模板參數(shù)修正之后的代碼:
// typedef __list_iterator<T, T&, T*> iterator;// typedef __list_iterator<T, const T&, const T*> const_iterator;template<class T, class Ref, class Ptr>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T, Ref, Ptr> Self;node* _pnode;__list_iterator(node*p):_pnode(p){}//返回數(shù)據(jù)的指針Ptr operator->(){return &_pnode->_data;}//模板參數(shù)做返回值Ref operator *(){return _pnode->_data;}//++itSelf& operator ++(){_pnode = _pnode->_next;return *this;}//it++Self operator ++(int){Self tmp(*this);_pnode = _pnode->_next;return tmp;}Self& operator--(){_pnode = _pnode->_prev;return *this;}Self operator--(int){Self tmp(*this);_pnode = _pnode->_prev;return tmp;}bool operator !=(const Self& it)const{return _pnode != it._pnode;}bool operator ==(const Self& it)const{return _pnode == it._pnode;}};
同一個類模板,此時我們傳遞不同的參數(shù)實(shí)例化成不同的迭代器了!!!這解決了我們剛剛所說的代碼冗余問題
四、增刪查改
4.1 insert(參數(shù)必須加引用,擔(dān)心非內(nèi)置類型)和erase
insert:在pos位置上一個插入,返回插入位置的迭代器,對于list的insert迭代器不會失效,vector失效是因?yàn)閿U(kuò)容導(dǎo)致pos位置造成野指針問題
iterator insert(iterator pos,const T& x){node* newnode = new node(x);node* cur = pos._pnode;node* prev = cur->_prev;newnode->_prev = prev;prev->_next = newnode;newnode->_next = cur;cur->_prev = newnode;++_size;return iterator(newnode);}
?erase:這里的帶頭(哨兵位)頭結(jié)點(diǎn)不可刪除,返回值是刪除位置的下一個,對于list的erase迭代器是失效的
iterator erase(iterator pos){assert(pos != end());node* prev = pos._pnode->_prev;node* next = pos._pnode->_next;prev->_next = next;next->_prev = prev;delete pos._pnode;--_size;return iterator(next);}
4.2 push_back和push_front
void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}
注意!list的begin和end的位置
同時這個問題還可以延伸出另一個問題:為什么迭代器訪問元素的時候要這樣寫?
?在vector中,物理地址是連續(xù)的,這么寫還情有可原,分析過list的begin和end之后,你還敢這么寫嗎??
?直接就報錯了,所以正確的應(yīng)該是!=,而不是 <
void test3()
{vector<int> vv={1,5,7,8,9,3,4};list<int> l={1,5,6,7};vector<int>::iterator it1=vv.begin();list<int>::iterator it2=l.begin();while(it1 < vv.end()){cout << *it1 << " ";it1++;}cout << endl;// while(it2 < l.end())// {// cout << *it2 << " ";// it2++;// }while(it2 != l.end()){cout << *it2 << " ";it2++;}cout << endl;
}
4.3??pop_back和pop_front
尾刪和頭刪,復(fù)用erase即可
void pop_front(){erase(begin());}void pop_back(){erase(--end());}
這里的尾刪剛好用上了我們的重載
五、list 構(gòu)造+賦值重載
5.1默認(rèn)構(gòu)造+迭代器區(qū)間構(gòu)造+拷貝構(gòu)造
默認(rèn)構(gòu)造:
list()
{_head = new node(T());_head->_next = _head;_head->_prev = _head;_size = 0;
}
我們可以用empty_initialize()來封裝初始化,方便復(fù)用,不用每次都寫:
void empty_initialize()
{_head = new node(T());_head->_next = _head;_head->_prev = _head;_size = 0;
}
迭代器區(qū)間構(gòu)造:
//迭代器區(qū)間構(gòu)造template <class InputIterator>list(InputIterator first, InputIterator last){empty_initialize();while (first != last){push_back(*first);++first;}}
拷貝構(gòu)造:
?傳統(tǒng):
?
list(const list<T>& lt){empty_initialize();for (const auto& e : lt){push_back(e);}}
?用范圍for進(jìn)行尾插,但是要注意要加上&,范圍for是*it賦值給給e,又是一個拷貝,e是T類型對象,依次取得容器中的數(shù)據(jù),T如果是string類型,不斷拷貝,push_back之后又銷毀
現(xiàn)代:
void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);} list(const list<T>& lt){empty_initialize();list<T> tmp(lt.begin(), lt.end());swap(tmp);}
5.2 賦值重載現(xiàn)代寫法
list<T>& operator=(list<T> lt){swap(lt);return *this;}
5.3 類名和類型的問題(C++的一個坑)
查看官方文檔,我們可以看到list沒有類型:
list<T>& operator=(list<T> lt)
list& operator=(list lt)?
對于普通類:類名等價于類型
對于類模板:類名不等價于類型(如list模板,類名:list 類型:list)
類模板里面可以用類名代表類型,但是并不建議,在類外面則必須要帶模板參數(shù)list
六、list和vector的對比(重點(diǎn))
vector | list | |
底層結(jié)構(gòu) | 動態(tài)順序表,一段連續(xù)空間 | 帶頭結(jié)點(diǎn)的雙向循環(huán)鏈表 |
隨機(jī)訪問 | 支持隨機(jī)訪問,訪問某個元素效率 O(1) | 不支持隨機(jī)訪問 |
插入和刪除 | 任意位置插入和刪除效率低,需要搬移元素,時間復(fù)雜度為 O(N),插入時有可能需要增容,增容:開辟新空間,拷貝元素,釋放舊空間,導(dǎo)致效率更低 | 任意位置插入和刪除效率高,不需要搬移元素,時間復(fù)雜度為 O(1) |
空間利用率 | 底層為連續(xù)空間,不容易造成內(nèi)存碎片,空間利用率高,緩存利用率高 | 底層節(jié)點(diǎn)動態(tài)開辟,小節(jié)點(diǎn)容易造成內(nèi)存碎片,空間利用率低,緩存利用率低 |
迭代器 | 原生態(tài)指針 | 對原生態(tài)指針 (節(jié)點(diǎn)指針) 進(jìn)行封裝 |
迭代器失效 | 在插入元素時,要給所有的迭代器重新賦值,因?yàn)椴迦朐赜锌赡軙?dǎo)致重新擴(kuò)容,致使原來迭代器失效,刪除時,當(dāng)前迭代器需要重新賦值否則會失效 | 插入元素不會導(dǎo)致迭代器失效,刪除元素時,只會導(dǎo)致當(dāng)前迭代器失效,其他迭代器不受影響 |
使用場景 | 在插入元素時,要給所有的迭代器重新賦值,因?yàn)椴迦朐赜锌赡軙?dǎo)致重新擴(kuò)容,致使原來迭代器失效,刪除時,當(dāng)前迭代器需要重新賦值否則會失效 | 大量插入和刪除操作,不關(guān)心隨機(jī)訪問 |
七、源碼合集
#pragma once#include <iostream>
#include <assert.h>
#include <algorithm>namespace lzy {template<class T>struct list_node //list 節(jié)點(diǎn)結(jié)構(gòu)定義{list_node<T>* _next;//不加<T>也沒錯,但是寫上好一些list_node<T>* _prev;T _data;list_node(const T& x)//構(gòu)造:_next(nullptr), _prev(nullptr), _data(x){}};//迭代器最終版//const 迭代器 -- 增加模板參數(shù),解決 operator*() 返回值與 operator->() 返回值問題//typedef __list_iterator<T, T&, T*> iterator;//typedef __list_iterator<T, const T&, const T*> const_iterator;//STL源碼中大佬的寫法,利用多個模板參數(shù)來避免副本造成的代碼冗余問題template<class T, class Ref, class Ptr>struct __list_iterator //迭代器類{typedef list_node<T> node; //重命名list節(jié)點(diǎn)typedef __list_iterator<T, Ref, Ptr> Self; //這里進(jìn)行重命名是為了后續(xù)再添加模板參數(shù)時只用修改這一個地方node* _pnode; //節(jié)點(diǎn)指針作為類的唯一成員變量__list_iterator(node* p):_pnode(p){}Ref operator*() //解引用{return _pnode->_data;}Ptr operator->() //->{return &_pnode->_data;}Self& operator++() //前置++{_pnode = _pnode->_next;return *this;}Self& operator++(int) //后置++{Self it(*this);_pnode = _pnode->_next;return it;}Self& operator--() //前置--{_pnode = _pnode->_prev;return *this;}Self& operator--(int) //后置--{Self it(*this);_pnode = _pnode->_prev;return it;}bool operator!=(const Self& it) const //!={return _pnode != it._pnode;}bool operator==(const Self& it) const //=={return _pnode == it._pnode;}};//list 類template<class T>class list{typedef list_node<T> node; //list 的節(jié)點(diǎn)public:typedef __list_iterator<T, T&, T*> iterator; //迭代器typedef __list_iterator<T, const T&, const T*> const_iterator; //const 迭代器//迭代器iterator begin() {return iterator(_head->_next);}iterator end() {//iterator it(_head);//return it;//直接利用匿名對象更為便捷return iterator(_head);}const_iterator begin() const {return const_iterator(_head->_next);}const_iterator end() const {return const_iterator(_head);}void empty_initialize() { //初始化 -- 哨兵位頭結(jié)點(diǎn)_head = new node(T());_head->_next = _head;_head->_prev = _head;_size = 0; //空間換時間,用于標(biāo)記節(jié)點(diǎn)個數(shù)}list() { //構(gòu)造,不是list<T>的原因:構(gòu)造函數(shù)函數(shù)名和類名相同,而list<T>是類型empty_initialize();}//迭代器區(qū)間構(gòu)造template <class InputIterator>list(InputIterator first, InputIterator last) {empty_initialize();while (first != last){push_back(*first);++first;//first++;}}//拷貝構(gòu)造傳統(tǒng)寫法//list(const list<T>& lt) {// empty_initialize();// for (const auto& e : lt)// {// push_back(e);// }//}// 拷貝構(gòu)造的現(xiàn)代寫法//list(const list& lt) 官方庫是這樣寫的,這是由于在類內(nèi)類名等價于類型,但不建議自己這樣寫list(const list<T>& lt) {empty_initialize(); //初始化頭結(jié)點(diǎn),防止交換后tmp野指針不能正常的調(diào)用析構(gòu)list<T> tmp(lt.begin(), lt.end());swap(tmp);}//賦值重載傳統(tǒng)寫法//list<T>& operator=(const list<T>& lt) {// if (this != <)// {// clear();// for (const auto& e : lt)// {// push_back(e);// }// }// return *this;//}//賦值重載現(xiàn)代寫法//list& operator=(list lt)list<T>& operator=(list<T> lt) { //不能加引用,lt是調(diào)用拷貝構(gòu)造生成的swap(lt);return *this;}~list() { //析構(gòu)clear();delete _head;_head = nullptr;}void swap(list<T>& lt) { //交換兩個鏈表,本質(zhì)上是交換兩個鏈表的頭結(jié)點(diǎn)std::swap(_head, lt._head);std::swap(_size, lt._size);}size_t size() const { //增加一個計數(shù)的成員,以空間換時間return _size;}bool empty() { //判空return _size == 0;}void clear() {iterator it = begin();while (it != end()) {it = erase(it);}_size = 0;}void push_back(const T& x) {//node* newnode = new node(x);//node* tail = _head->_prev;//tail->_next = newnode;//newnode->_prev = tail;//newnode->_next = _head;//_head->_prev = newnode;insert(end(), x); //復(fù)用}void push_front(const T& x) {insert(begin(), x); //復(fù)用}void pop_front() {erase(begin());}void pop_back() {erase(--end());}iterator insert(iterator pos, const T& x) {node* newnode = new node(x);node* cur = pos._pnode;node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;cur->_prev = newnode;newnode->_next = cur;++_size;return iterator(pos);}iterator erase(iterator pos) {assert(pos != end());node* prev = pos._pnode->_prev;node* next = pos._pnode->_next;prev->_next = next;next->_prev = prev;delete pos._pnode;--_size;return iterator(next);}private:node* _head;size_t _size;};
}
?
完結(jié)撒花~