目錄🌈感謝閱讀East-sunrise學習分享——list的介紹及模擬實現(xiàn)
成都創(chuàng)新互聯(lián)公司是少有的成都網(wǎng)站設計、網(wǎng)站建設、營銷型企業(yè)網(wǎng)站、重慶小程序開發(fā)、手機APP,開發(fā)、制作、設計、賣友情鏈接、推廣優(yōu)化一站式服務網(wǎng)絡公司,從2013年成立,堅持透明化,價格低,無套路經營理念。讓網(wǎng)頁驚喜每一位訪客多年來深受用戶好評博主水平有限,如有差錯,歡迎斧正🙏感謝有你
碼字不易,若有收獲,期待你的點贊關注💙我們一起進步
今天想分享介紹一下STL的容器之一list,以及進行模擬實現(xiàn)📌
有了之前對數(shù)據(jù)結構——鏈表的知識基礎,其實list的底層結構也并不神秘了,而list相較于前面的string、vector容器特別的地方就在于其迭代器,今天我們重點放在list的模擬實現(xiàn)及迭代器的介紹
list底層結構是帶頭雙向鏈表
templatestruct list_node
{list_node* _next;//指向下一個節(jié)點
list_node* _prev;//指向前一個節(jié)點
T _data;//節(jié)點數(shù)據(jù)
//構造函數(shù)
list_node(const T& x)
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{}
};
定義完list的節(jié)點,接下來即是list的主要結構
namespace qdy
{templatestruct list_node
{list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)
:_next(nullptr)
, _prev(nullptr)
, _data(x)
{}
};
templateclass list
{typedef list_nodenode;
public:
list()
{ _head = new node(T());
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
//尾插
void push_back(const T& x)
{ node* newnode = new node(x);
node* tail = _head->_prev;
// _head tail newnode
tail->_next = newnode;
newnode->_prev = tail;
newnode->_next = _head;
_head->_prev = newnode;
}
private:
node* _head;//哨兵位的頭節(jié)點
size_t _size;//記錄節(jié)點個數(shù)
};
以上便是list的結構框架(簡易版實現(xiàn)),目前僅有尾插的接口,用于對list容器中添加數(shù)據(jù)??
添加數(shù)據(jù)后我們想要遍歷打印怎么辦呢?那就需要用到STL六大組件之一的迭代器🧮
iterator的模式定義:“提供一種方法,使之能夠依序巡訪某個聚合物(容器)所含的各個元素,而又無需暴露該聚合物的內部表述方式。”
——《STL源碼剖析》
🎈通俗理解:容器用于存放數(shù)據(jù),存放數(shù)據(jù)便有訪問數(shù)據(jù)讀寫數(shù)據(jù)的需求,STL六大組件之一的迭代器,便是給每個容器提供一個便于讀取數(shù)據(jù)的方法
2.迭代器的功能分類迭代器是內嵌類型,通常定義為內部類或者直接定義在類中
3.迭代器失效對于list,迭代器在進行insert操作后不失效,在進行erase操作后失效?
前面說過,此處大家可將迭代器暫時理解成類似于指針,迭代器失效即迭代器所指向的節(jié)點無效,即該節(jié)點被刪除了。因為list的底層結構為帶頭結點的雙向循環(huán)鏈表,因此在list中進行插入時是不會導致list的迭代器失效的,只有在刪除時才會失效,并且失效的只是指向被刪除節(jié)點的迭代器,其他迭代器不會受到影響。
void TestListIterator1()
{int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
listl(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{// erase()函數(shù)執(zhí)行后,it所指向的節(jié)點已被刪除,因此it無效,在下一次使用it時,必須先給其賦值
l.erase(it);
++it;
}
}
// 改正
void TestListIterator()
{int array[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
listl(array, array+sizeof(array)/sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{it = l.erase(it);
//為了避免迭代器失效的問題,erase接口提供了返回值可接收,該返回值為刪除后的下一節(jié)點
}
}
4.list迭代器的模擬實現(xiàn)
1.普通迭代器在對迭代器模擬實現(xiàn)之前,我們要搞清楚list迭代器要有什么功能?
回顧之前string和vector迭代器的模擬實現(xiàn),我們是直接將指針typedef為迭代器使用,因為string和vector的底層結構是順序表,是一段連續(xù)的物理空間,所以通過使用原生指針便能符合其迭代器的需求了??
💥但是list的底層結構是鏈表,鏈表是按需開辟空間,并不是一段連續(xù)的物理空間,每個節(jié)點的物理地址并不連續(xù),我們無法直接使用原生指針去+±-來遍歷來訪問節(jié)點。我們回顧剛開始接觸C++學習的日期類,日期類中的“日期”是我們自己定義的一種自定義類型,無法使用內置操作符直接對日期進行運算操作(編譯器又不認識那么多)所以我們是通過自己再去重定義日期的操作類,去重載操作符來滿足需求。
🚩類和對象說:該我上場表演了
既然原生指針已經無法滿足list迭代器的需求,那么我們可以自己定義一個迭代器,然后將節(jié)點指針封裝起來,然后再根據(jù)我們具體的需求情況去重載各種運算符實現(xiàn)迭代器功能。
//用類封裝迭代器
templatestruct __list_iterator
{typedef list_nodenode;
node* _pnode;//封裝一個節(jié)點的指針
//用節(jié)點指針進行構造
__list_iterator(node* p)
:_pnode(p)
{}
//迭代器運算符的重載
T& operator*()
{return _pnode->_data;
}
__list_iterator& operator++()
{_pnode = _pnode->_next;
return *this;//返回的是迭代器
}
bool operator!=(const __list_iterator& it)
{return _pnode != it._pnode;
}
};
定義完迭代器后便能對我們添加了數(shù)據(jù)的list進行遍歷打印了
1.迭代器遍歷 2.范圍for遍歷(底層也是調用了迭代器)
void test_list1()
{listlt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
list::iterator it = lt.begin();
while (it != lt.end())
{cout<< *it<< " ";
++it;
}
cout<< endl;
for (auto e : lt)
{cout<< e<< " ";
}
cout<< endl;
}
定義完迭代器后,通過迭代器對容器數(shù)據(jù)進行訪問,實際上是一種函數(shù)調用
2.const迭代器?const迭代器的錯誤寫法
typedef __list_iteratoriterator;
const list::iterator cit = lt.begin();
const之前我們修飾指針時有兩種方法
const T* p1;
T* const p2;
正確的const迭代器應該是像p1的行為,保護指向的對象不被修改,但是迭代器本身是可以修改的
但是上述的const迭代器寫法是保護了迭代器本身不能被修改,那么我們就不能++迭代器了
??正確寫法:想實現(xiàn)const迭代器,無法對普通迭代器直接加const,需要再寫一個const版本迭代器的類
templatestruct __list_const_iterator
{typedef list_nodenode;
node* _pnode;
__list_const_iterator(node* p)
:_pnode(p)
{}
const T& operator*()const
{return _pnode->_data;
}
__list_const_iterator& operator++()
{_pnode = _pnode->_next;
return *this;
}
__list_const_iterator& operator--()
{_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const __list_const_iterator& it)
{return _pnode != it._pnode;
}
};
typedef __list_iteratorconst_iterator;
但是如果像上述一樣,寫一個普通迭代器再寫一個const迭代器,代碼看起來就十分的冗長。那么我們可以利用好類模板,類模板即是能根據(jù)模板和調用時的參數(shù),根據(jù)實參類型推演產生函數(shù)的特定類型版本。這樣,我們根據(jù)傳入?yún)?shù)的不同,可以使得一份類模板生成兩種類型的迭代器🧮
templatestruct __list_iterator
{typedef list_nodenode;
typedef __list_iteratorSelf;
node* _pnode;
__list_iterator(node* p)
:_pnode(p)
{}
Ref operator*()
{return _pnode->_data;
}
Self& operator++()
{_pnode = _pnode->_next;
return *this;
}
Self& operator--()
{_pnode = _pnode->_prev;
return *this;
}
bool operator!=(const Self& it)
{return _pnode != it._pnode;
}
};
typedef __list_iteratoriterator;
typedef __list_iteratorconst_iterator;
5.迭代器operator->的重載我們調用對象的成員變量成員函數(shù)是用.
實現(xiàn),對指針解引用取其值是用*
實現(xiàn),而當結構體要解引用是使用->
,再用->
取其成員變量。而假如現(xiàn)在list中就存放的是結構體類型的數(shù)據(jù)??
所以我們有必要對->
也進行重載
T* operator->()
{return &_pnode->_data;
}
重載完之后我們要怎么使用呢?一共有3種方法
//坐標類
struct Pos
{int _row;
int _col;
Pos(int row = 0, int col = 0)
:_row(row)
,_col(col)
{}
};
void test()
{listlt;
lt.push_back(Pos(1,1));
lt.push_back(Pos(2,2));
lt.push_back(Pos(3,3));
// int* p --->*p
// Pos* p --->p->list::iterator it = lt.begin();
while (it != lt.end())
{cout<< (&(*it))->_row;
//*it取出容器數(shù)據(jù)(POS類) -- 再取地址訪問解引用得到_row
cout<< it.operator->()->_row;
//it.operator->()是顯式調用,然后再->解引用得到_row
cout<< it->_row;
//同第二種寫法,編譯器為了可讀性,省略了一個->
++it;
}
}
💥但是當實際使用時,會發(fā)現(xiàn)有問題,那就是->的重載返回值為T*,這樣一來無論是普通迭代器或const迭代器都能對其值進行修改,所以我們需要將operator->
的返回值改為泛型,然后針對不同的迭代器給不同的返回參數(shù)以示區(qū)分,如此一來,我們的迭代器模板又得再多一個參數(shù)啦📈
templatePtr operator->()
{return &_pnode->_data;
}
typedef __list_iteratoriterator;
typedef __list_iteratorconst_iterator;
6.迭代器的價值vector和list就像左右手一樣,是互補配合的關系
vector的優(yōu)點即是list的缺點,list的優(yōu)點也是vector的缺點,實際使用時可按照需求擇優(yōu)選用或者結合使用
1.vectorvector的優(yōu)點
🎈綜上所述vector的優(yōu)點都得益于其結構優(yōu)勢
vector的缺點
🎈迭代器失效
insert和erase均會導致迭代器失效
2.listlist的優(yōu)點
list的缺點
🎈迭代器失效
insert不失效,erase失效
namespace qdy
{templatestruct list_node
{list_node* _next;
list_node* _prev;
T _data;
list_node(const T& x)
:_next(nullptr)
,_prev(nullptr)
,_data(x)
{}
};
templatestruct __list_iterator
{typedef list_nodenode;
typedef __list_iteratorSelf;
node* _pnode;
__list_iterator(node* p)
:_pnode(p)
{}
Ptr operator->()
{ return &_pnode->_data;
}
Ref operator*()
{ return _pnode->_data;
}
Self& operator++()
{ _pnode = _pnode->_next;
return *this;
}
Self& operator--()
{ _pnode = _pnode->_prev;
return *this;
}
Self operator--(int)
{ Self tmp(*this);
_pnode = _pnode->_prev;
return tmp;
}
bool operator!=(const Self& it)
{ return _pnode != it._pnode;
}
bool operator==(const Self& it) const
{ return _pnode == it._pnode;
}
};
templateclass list
{typedef list_nodenode;
public:
typedef __list_iteratoriterator;
//typedef __list_const_iteratorconst_iterator;
typedef __list_iteratorconst_iterator;
//構造函數(shù)
list()
{ empty_initialize();
}
~list()
{ clear();
delete _head;
_head = nullptr;
}
void clear()
{ iterator it = begin();
while (it != end())
{ it = erase(it);
}
}
const_iterator begin() const
{ return const_iterator(_head->_next);
}
const_iterator end() const
{ return const_iterator(_head);
}
iterator begin()
{ return iterator(_head->_next);
//iterator it(_head->_next);
//return it;
}
iterator end()
{ return iterator(_head);
}
void empty_initialize()
{ _head = new node(T());
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
templatelist(InputIterator first, InputIterator last)
{ empty_initialize();
while (first != last)
{ push_back(*first);
++first;
}
}
void swap(list& lt)
{ std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
// 現(xiàn)代寫法
// lt2(lt1)
list(const list& lt)
//list(const list& lt) // 不建議
{ empty_initialize();
listtmp(lt.begin(), lt.end());
swap(tmp);
}
// lt3 = lt1
list& operator=(listlt)
//list& operator=(list lt) // 不建議
{ swap(lt);
return *this;
}
//尾插
void push_back(const T& x)
{ //node* newnode = new node(x);
//node* tail = _head->_prev;
_head tail newnode
//newnode->_prev = tail;
//tail->_next = newnode;
//newnode->_next = _head;
//_head->_prev = newnode;
insert(end(), x);
}
void push_front(const T& x)
{ insert(begin(), x);
}
void pop_back()
{ erase(--end());
}
void pop_front()
{ erase(begin());
}
iterator insert(iterator pos, const T& x)
{ node* newnode = new node(x);
node* cur = pos._pnode;
node* prev = cur->_prev;
//prev newnode cur
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = cur;
cur->_prev = newnode;
++_size;
return iterator(newnode);
}
iterator erase(iterator pos)
{ assert(pos != _head);
node* prev = pos._pnode->_prev;
node* next = pos._pnode->_next;
prev->_next = next;
next->_prev = prev;
delete pos._pnode;
--_size;
return iterator(next);
}
size_t size()const
{ return _size;
}
bool empty()const
{ return _size == 0;
}
private:
node* _head;
size_t _size;
};
}
🌈🌈寫在最后
我們今天對list的分享就到此結束了
對于這篇博客最精華的部分便是迭代器的實現(xiàn),迭代器在各種容器中是不可或缺的一部分🚩
🎈感謝能耐心地閱讀到此
🎈碼字不易,感謝三連
🎈關注博主,我們一起學習、一起進步
你是否還在尋找穩(wěn)定的海外服務器提供商?創(chuàng)新互聯(lián)www.cdcxhl.cn海外機房具備T級流量清洗系統(tǒng)配攻擊溯源,準確流量調度確保服務器高可用性,企業(yè)級服務器適合批量采購,新人活動首月15元起,快前往官網(wǎng)查看詳情吧
當前名稱:【C++】list的介紹及模擬實現(xiàn)-創(chuàng)新互聯(lián)
瀏覽地址:http://muchs.cn/article16/idjgg.html
成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供用戶體驗、品牌網(wǎng)站建設、定制開發(fā)、全網(wǎng)營銷推廣、靜態(tài)網(wǎng)站、響應式網(wǎng)站
聲明:本網(wǎng)站發(fā)布的內容(圖片、視頻和文字)以用戶投稿、用戶轉載內容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內容未經允許不得轉載,或轉載時需注明來源: 創(chuàng)新互聯(lián)
猜你還喜歡下面的內容