父母就像迭代器,封裝了他們的脆弱......?
手撕list目錄:
一、list的常用接口及其使用
1.1list 構(gòu)造函數(shù)與增刪查改
1.2list 特殊接口
1.3list 排序性能分析
二、list 迭代器實現(xià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ù)必須加引用,擔心非內(nèi)置類型)和erase
4.2 push_back和push_front
4.3??pop_back和pop_front
五、list 構(gòu)造+賦值重載
5.1默認構(gòu)造+迭代器區(qū)間構(gòu)造+拷貝構(gòu)造
5.2 賦值重載現(xiàn)代寫法
5.3 類名和類型的問題(C++的一個坑)
六、list和vector的對比(重點)
七、源碼合集
一、list的常用接口及其使用
1.1list 構(gòu)造函數(shù)與增刪查改
list 是可以在常數(shù)范圍內(nèi)在任意位置進行插入和刪除的序列式容器,其底層是帶頭雙向循環(huán)鏈表;list 常用接口的使用和 string、vector 系列容器的接口使用一樣,這里我不詳細介紹,請看我們的老朋友: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é)點地址和后一個節(jié)點地址的位置關(guān)系是隨機的,所以 list 不支持隨機訪問,自然也就不支持 [ ] 操作;
2、list 不支持reserve操作,因為 list 的節(jié)點是使用時開辟,使用完銷毀,不能預留空間;
(從這個特點也容易看出來,如果需要一直插入刪除元素,利用list更好)
1.2list 特殊接口
除了上述?STL 容器基本都有的一般接口外,list 還提供一些獨有的特殊操作接口,如下:
函數(shù)聲明 | 接口說明 |
---|---|
splice | 將 list1 中的元素轉(zhuǎn)移到 list2 中 |
remove | 移除 list 中的指定元素 |
unique | 鏈表去重(sort之后才可以用) |
merge | 合并兩個鏈表 |
sort | 鏈表排序(探究為什么list自己寫sort) |
reverse | 鏈表逆置 |
題外話: 為什么list需要自己實現(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;
}
報錯了(意外之中,如果不報錯我還寫這個知識點干啥 doge) 報錯原因說沒有-迭代器
讓我們看看sort源碼~
這一切的一切都是因為sort的迭代器引起的!!?
注意:
1、鏈表排序只能使用 list 提供的 sort 接口,而不能使用 algorithm 提供的 sort 接口,因為鏈表物理地址不連續(xù),迭代器為雙向迭代器,不支持 + - 操作,而算法庫中的 sort 函數(shù)需要支持 + - 的隨機迭代器;
2、鏈表去重之前必須保證鏈表有序,否則去重不完全;
3、兩個有序鏈表合并之后仍然保存有序;
?最后,雖然 list 提供了這些具有特殊功能的接口,它們也確實有一定的作用,但是實際上這些特殊接口使用頻率非常低,包括 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 sort
int begin1 = clock();
sort(v.begin(), v.end());
int end1 = clock();
//list sort
int begin2 = clock();
lt.sort();
int end2 = clock();
printf("vector sort:%d\n", end1 - begin1);
printf("list sort:%d\n", end2 - begin2);
}
測試二:list 直接進行排序與將數(shù)據(jù)拷貝到 vector 中使用 vector 排序后再將數(shù)據(jù)拷回 list 中性能對比
//list sort 與 將數(shù)據(jù)轉(zhuǎn)移到 vector 中進行排序后拷貝回來性能對比 -- 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 -- lt1
int begin1 = clock();
lt1.sort();
int end1 = clock();
// 將數(shù)據(jù)拷貝到vector中排序,排完以后再拷貝回來 -- lt2
int 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 的效率遠低于 vector sort,甚至于說,直接使用 list sort 的效率都不如先將數(shù)據(jù)拷貝到 vector 中,然后使用 vector sort,排序之后再將數(shù)據(jù)拷貝回 list 中快;所以list中的sort接口是很挫的??!
二、list 迭代器實現(xiàn)(重點+難點)
關(guān)于迭代器的引入知識:
迭代器的價值在于封裝底層的實現(xiàn),不具體暴露底層的實現(xiàn)細節(jié),提供統(tǒng)一的訪問方式
iterator只是代言人?。≌嬲呐1拼罄衅鋵嵤莀list_iterator
為什么在 list 中將迭代器搞成指針這招不好用了呢??
在數(shù)組中,*指針就是元素,指針++就是 +sizeof(T) 對象大小,沒辦法,誰叫他們物理空間連續(xù),結(jié)構(gòu)NB,所以對于vector和string類而言,物理空間是連續(xù)的,原生的指針就是迭代器了,解引用就是數(shù)據(jù)了。但是對于這里的list而言,空間是不連續(xù)的
解決方法:
此時如果解引用是拿不到數(shù)據(jù)的(空間不連續(xù)),更不用說++指向下一個結(jié)點了。所以,對于list的迭代器,原生指針已經(jīng)不符合我們的需求了,我們需要去進行特殊處理:進行類的封裝。我們可以通過類的封裝以及運算符重載支持,這樣就可以實現(xiàn)像內(nèi)置類型一樣的運算符
迭代器的倆個特征:
1.解引用2.++ / --
運算符重載的大任務:
實現(xiàn)解引用operator*()和++函數(shù)
2.1迭代器的分類
按照迭代器的功能,迭代器一共可以分為以下三類:
- 單向迭代器 – 迭代器僅僅支持 ++ 和解引用操作(單鏈表,哈希)
- 雙向迭代器 – 迭代器支持 ++、-- 和解引用操作,但不支持 +、- 操作(list 雙向鏈表)
- 隨機迭代器 – 迭代器不僅支持 ++、-- 和解引用操作,還支持 +、- 操作,即迭代器能夠隨機訪問(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迭代器的行為,因為保護迭代器本身不能修改,那么我們也就不能++迭代器
靈魂拷問:const迭代器是p1還是p2?p1
const迭代器類似p1的行為,保護指向的對象不被修改,迭代器本身可以修改
2.2?list 迭代器失效問題(和vector有差異)
vector迭代器失效:insert擴容+erase的時候會失效
和 vector 不同,list 進行 insert 操作后并不會產(chǎn)生迭代器失效問題,因為 list 插入的新節(jié)點是動態(tài)開辟的,同時由于 list 每個節(jié)點的物理地址是不相關(guān)的,所以插入的新節(jié)點并不會影響原來其他節(jié)點的地址;
但是 list erase 之后會發(fā)生迭代器失效,因為 list 刪除節(jié)點會直接將該節(jié)點釋放掉,此時我們再訪問該節(jié)點就會造成越界訪問
2.3list 迭代器源碼模板
我們知道,迭代器是類似于指針一樣的東西,即迭代器要能夠?qū)崿F(xiàn)指針相關(guān)的全部或部分操作 – ++、–、*、+、-;對我們之前 string 和 vector 的迭代器來說,迭代器就是原生指針,所以它天然的就支持上述操作;
但是對于 list 來說,list 的節(jié)點是一個結(jié)構(gòu)體,同時 list 每個節(jié)點的物理地址是不連續(xù)的,如果此時我們還簡單將節(jié)點的指針 typedef 為迭代器的話,那么顯然它是不能夠?qū)崿F(xiàn)解引用、++ 等操作的,所以我們需要用結(jié)構(gòu)體/類來對迭代器進行封裝,再配合運算符重載等操作讓迭代器能夠?qū)崿F(xiàn)解引用、++、-- 等操作
框架代碼如下:
//節(jié)點定義
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é)點的指針
link_type node; //類成員變量
__list_iterator(link_type x) : node(x) {} //將節(jié)點指針構(gòu)造為類對象
//... 使用運算符重載支持迭代器的各種行為
self& operator++() {...}
self& operator--() {...}
Ref operator*() const {...}
};
2.4list 整體基本框架
namespace lzy
{
//結(jié)點
template<class T>
struct list_node
{
list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)//節(jié)點的構(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迭代器
迭代器的實現(xiàn)我們需要去考慮普通迭代器和const迭代器。這兩種迭代器的不同,也會帶來不同的接口。我們可以分別單獨去進行實現(xiàn),我們先來看一看簡單的構(gòu)造迭代器,只需要提供一個結(jié)點即可,看一看實現(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)造和賦值重載我們并不需要自己去手動實現(xiàn),編譯器默認生成的就是淺拷貝,而我們需要的就是淺拷貝,這也說明了,并不是說如果有指針就需要我們?nèi)崿F(xiàn)深拷貝,而且迭代器不需要寫析構(gòu)函數(shù),所以說不需要深拷貝
為什么聊這個問題?因為list<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;
}
};
代碼冗余?。?!代碼冗余!??!代碼冗余?。?!
如果是這樣子去實現(xiàn)的話,我們就會發(fā)現(xiàn),這兩個迭代器的實現(xiàn)并沒有多大的區(qū)別,唯一的區(qū)別就在于operator*的不同。const迭代器和普通迭代器的唯一區(qū)別就是普通迭代器返回T&,可讀可寫,const迭代器返回const T&,可讀不可寫。我們可以參考源碼的實現(xiàn):類模板參數(shù)解決這個問題,這也是迭代器的強大之處
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;
}
//++it
Self& 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ù)實例化成不同的迭代器了!?。∵@解決了我們剛剛所說的代碼冗余問題
四、增刪查改
4.1 insert(參數(shù)必須加引用,擔心非內(nèi)置類型)和erase
insert:在pos位置上一個插入,返回插入位置的迭代器,對于list的insert迭代器不會失效,vector失效是因為擴容導致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é)點不可刪除,返回值是刪除位置的下一個,對于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之后,你還敢這么寫嗎??
?直接就報錯了,所以正確的應該是!=,而不是 <
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
尾刪和頭刪,復用erase即可
void pop_front()
{
erase(begin());
}
void pop_back()
{
erase(--end());
}
這里的尾刪剛好用上了我們的重載
五、list 構(gòu)造+賦值重載
5.1默認構(gòu)造+迭代器區(qū)間構(gòu)造+拷貝構(gòu)造
默認構(gòu)造:
list()
{
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
我們可以用empty_initialize()來封裝初始化,方便復用,不用每次都寫:
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進行尾插,但是要注意要加上&,范圍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的對比(重點)
vector | list | |
底層結(jié)構(gòu) | 動態(tài)順序表,一段連續(xù)空間 | 帶頭結(jié)點的雙向循環(huán)鏈表 |
隨機訪問 | 支持隨機訪問,訪問某個元素效率 O(1) | 不支持隨機訪問 |
插入和刪除 | 任意位置插入和刪除效率低,需要搬移元素,時間復雜度為 O(N),插入時有可能需要增容,增容:開辟新空間,拷貝元素,釋放舊空間,導致效率更低 | 任意位置插入和刪除效率高,不需要搬移元素,時間復雜度為 O(1) |
空間利用率 | 底層為連續(xù)空間,不容易造成內(nèi)存碎片,空間利用率高,緩存利用率高 | 底層節(jié)點動態(tài)開辟,小節(jié)點容易造成內(nèi)存碎片,空間利用率低,緩存利用率低 |
迭代器 | 原生態(tài)指針 | 對原生態(tài)指針 (節(jié)點指針) 進行封裝 |
迭代器失效 | 在插入元素時,要給所有的迭代器重新賦值,因為插入元素有可能會導致重新擴容,致使原來迭代器失效,刪除時,當前迭代器需要重新賦值否則會失效 | 插入元素不會導致迭代器失效,刪除元素時,只會導致當前迭代器失效,其他迭代器不受影響 |
使用場景 | 在插入元素時,要給所有的迭代器重新賦值,因為插入元素有可能會導致重新擴容,致使原來迭代器失效,刪除時,當前迭代器需要重新賦值否則會失效 | 大量插入和刪除操作,不關(guān)心隨機訪問 |
七、源碼合集
#pragma once
#include <iostream>
#include <assert.h>
#include <algorithm>
namespace lzy {
template<class T>
struct list_node //list 節(jié)點結(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é)點
typedef __list_iterator<T, Ref, Ptr> Self; //這里進行重命名是為了后續(xù)再添加模板參數(shù)時只用修改這一個地方
node* _pnode; //節(jié)點指針作為類的唯一成員變量
__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é)點
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é)點
_head = new node(T());
_head->_next = _head;
_head->_prev = _head;
_size = 0; //空間換時間,用于標記節(jié)點個數(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é)點,防止交換后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é)點
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); //復用
}
void push_front(const T& x) {
insert(begin(), x); //復用
}
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;
};
}
?
文章來源:http://www.zghlxwxcb.cn/news/detail-714050.html
完結(jié)撒花~文章來源地址http://www.zghlxwxcb.cn/news/detail-714050.html
到了這里,關(guān)于【C++】list基本接口+手撕 list(詳解迭代器)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!