C++?list常用接口和模擬實(shí)現(xiàn)實(shí)例代碼
C++中l(wèi)ist容器底層實(shí)現(xiàn)是使用帶頭雙向循環(huán)鏈表的結(jié)構(gòu),通過(guò)指針指向前一個(gè)和后一個(gè)節(jié)點(diǎn)。它也具有雙向鏈表的優(yōu)缺點(diǎn),比如優(yōu)點(diǎn)是對(duì)于在任意位置插入和刪除不用移動(dòng)數(shù)據(jù),缺點(diǎn)是不能任意位置的隨機(jī)訪問(wèn),必須遍歷到要訪問(wèn)的節(jié)點(diǎn)才可以;并且list還需要額外的空間來(lái)存儲(chǔ)前一個(gè)和后一個(gè)節(jié)點(diǎn)的信息。
下面了解一下list的常用接口
1.構(gòu)造函數(shù)
//構(gòu)造空的list list() //拷貝構(gòu)造 list (const list& x); //迭代器構(gòu)造,使用模板能適合不同的類(lèi)型 template <class InputIterator> list (InputIterator first, InputIterator last); //構(gòu)造n個(gè)值為val的list list (size_type n, const value_type& val = value_type())
2.迭代器
//返回開(kāi)始位置的迭代器 iterator begin(); const_iterator begin() const; //返回結(jié)束位置的迭代器 iterator end(); const_iterator end() const;
需要注意的是這里的迭代器在底層并不是原生指針,具體實(shí)現(xiàn)在后文給出。
3.容量操作
//判斷是否為空 bool empty() const; //返回有效節(jié)點(diǎn)的個(gè)數(shù) size_type size() const;
4.元素獲取
//返回首元素的引用 reference front(); const_reference front() const; //返回末尾元素的引用 reference back(); const_reference back() const;
5.增刪改查
//頭插 void push_front (const value_type& val); //尾插 void push_back (const value_type& val); //頭刪 void pop_front(); //尾刪 void pop_back(); //在任意位置插入 iterator insert (iterator position, const value_type& val); //在任意位置刪除 iterator erase (iterator position); //交換兩個(gè)鏈表中的元素 void swap (list& x); //清空鏈表中的元素 void clear();
在了解了鏈表的增刪查改之后我們思考一個(gè)問(wèn)題
list是否會(huì)像vector和string一樣出現(xiàn)迭代器失效的原因?由于這里是鏈?zhǔn)浇Y(jié)構(gòu),它的內(nèi)存在物理空間上并不連續(xù),因此如果僅僅是插入一個(gè)元素并不會(huì)出現(xiàn)迭代器失效的問(wèn)題,只有刪除時(shí)會(huì)發(fā)生迭代器失效的問(wèn)題,并且這里只有刪除節(jié)點(diǎn)的迭代器會(huì)失效。
下面給出list的模擬實(shí)現(xiàn)
1.節(jié)點(diǎn)list_node設(shè)計(jì)
節(jié)點(diǎn)的結(jié)構(gòu)就是雙向鏈表的結(jié)構(gòu),這里也可以寫(xiě)成結(jié)構(gòu)直接默認(rèn)類(lèi)成員公有的也可以。
template<class T>
class list_node
{
public:
list_node<T>* _next;
list_node<T>* _prev;
T _val;
list_node(const T& val = T())//默認(rèn)匿名對(duì)象
:_next(nullptr)
,_prev(nullptr)
,_val(val)
{}
};2.迭代器設(shè)計(jì)
關(guān)于const的迭代器設(shè)計(jì)與之前的string和vector都有所不同,對(duì)于節(jié)點(diǎn)的指針進(jìn)行了又一層的封裝。因?yàn)檫@里要支持->箭頭和的重載。我們對(duì)于一個(gè)指針變量進(jìn)行解引用通常是要訪問(wèn)其中存儲(chǔ)的數(shù)據(jù)的內(nèi)容(_val);而->是要訪問(wèn)數(shù)據(jù)內(nèi)容的成員。從這個(gè)特性就可以得到一個(gè)結(jié)論,在實(shí)現(xiàn)list迭代器時(shí),重載解引用和箭頭的返回類(lèi)型不同,那么我們要怎樣控制呢?
事實(shí)上在這里只需要控制模板參數(shù)就可以實(shí)現(xiàn)這種效果。具體代碼如下
template<class T,class Ref ,class Ptr>
class _list_iterator
{
public:
//這里事實(shí)上迭代器只是借用節(jié)點(diǎn)進(jìn)行訪問(wèn),而不是管理這些節(jié)點(diǎn),鏈表才管理節(jié)點(diǎn)
typedef list_node<T> Node;
typedef _list_iterator<T, Ref,Ptr> self;
Node* _node;
_list_iterator(Node* node)
:_node(node)
{}
Ref operator*()//這里控制解引用的返回類(lèi)型
{
return _node->_val;
}
Ptr operator->()
{
return &_node->_val;
}
//前置++
self& operator++()
{
_node = _node->_next;
return *this;
}
//后置++
self operator++(int)
{
self temp(*this);
_node = _node->_next;
return temp;
}
//前置--
self& operator--()
{
_node = _node->_prev;
return *this;
}
//后置--
self operator--(int)
{
self temp(*this);
_node = _node->_prev;
return temp;
}
bool operator!=(const self& it) const
{
return _node != it._node;
}
bool operator==(const self& it) const
{
return _node == it._node;
}
};這里的Ref控制解引用的返回類(lèi)型,Ptr控制箭頭的返回類(lèi)型;并且在這個(gè)類(lèi)中利用typedef的方法控制自增和自減的返回類(lèi)型。
并且這樣寫(xiě)還有一點(diǎn)好處是,對(duì)于普通的迭代器使用正常的模板參數(shù)即可,對(duì)于const迭代器使用const的模板參數(shù)即可。
3.常用接口的模擬實(shí)現(xiàn)
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef _list_iterator<T,T&,T*> iterator;//內(nèi)嵌類(lèi)
typedef _list_iterator<T,const T&,const T*> const_iterator;
//這里const迭代器的設(shè)計(jì)是錯(cuò)誤的,因?yàn)閏onst迭代器是希望指向內(nèi)容不被修改
//這樣的設(shè)計(jì)是迭代器本身不能修改
//typedef const _list_iterator<T> const_iterator;
iterator begin()
{
return _head->_next;//單參數(shù)的構(gòu)造函數(shù)支持隱式類(lèi)型轉(zhuǎn)換
}
const_iterator begin() const
{
return _head->_next;
}
iterator end()
{
return _head;
}
const_iterator end() const
{
return _head;
}
void empty_init()
{
_head = new Node;
_head->_prev = _head;
_head->_next = _head;
}
list()
{
empty_init();
}
//lt2(lt1)
list(const list<T>& lt)
//list(const list& lt)//不推薦這樣使用
{
empty_init();
//遍歷lt1,直接把lt2尾插到lt1
//這里傳引用效率高
for (const auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
}
list<T>& operator=(list<T> lt)
//list& operator=(list lt)//這里與上面的拷貝構(gòu)造同理
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
iterator it = begin();
while (it != end())
{
it = erase(it);
}
}
void push_back(const T& x)
{
insert(end(), x);
}
void pop_back()
{
erase(--end());//這里調(diào)用--,頭節(jié)點(diǎn)的prev就是尾
}
void push_front(const T& x)
{
insert(begin(), x);
}
void pop_front()
{
erase(begin());
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
prev->_next = newnode;
newnode->_next = cur;
cur->_prev = newnode;
newnode->_prev = prev;
return newnode;
}
iterator erase(iterator pos)//這里會(huì)有迭代器失效,返回下一個(gè)位置的迭代器
{
assert(pos != end());
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* next = cur->_next;
prev->_next = next;
next->_prev = prev;
delete cur;
return next;
}
size_t size()
{
size_t sz = 0;
iterator it = begin();
while (it != end())
{
++sz;
++it;
}
return sz;
}
private:
Node* _head;
};這里的常用接口實(shí)現(xiàn)都類(lèi)似與數(shù)據(jù)結(jié)構(gòu)中的帶頭的雙向鏈表。需要注意的就是爹迭代器失效的問(wèn)題。
最后總結(jié)一下vector和list的各自?xún)?yōu)劣
(1)底層結(jié)構(gòu):vector是連續(xù)存儲(chǔ)的,是一個(gè)動(dòng)態(tài)的順序表;list是不連續(xù)存儲(chǔ),是一個(gè)帶頭的雙向循環(huán)鏈表
(2)隨機(jī)訪問(wèn):vector支持[]隨機(jī)訪問(wèn);list不支持隨機(jī)訪問(wèn)
(3)插入和刪除效率:vector的插入和刪除需要移動(dòng)數(shù)據(jù);list的插入和刪除不需要移動(dòng)數(shù)據(jù)。
(4)空間利用率:vector底層是連續(xù)的存儲(chǔ)空間,不容易造成內(nèi)存碎片的問(wèn)題,空間利用率高;list底層是不連續(xù)的存儲(chǔ)空間,小的節(jié)點(diǎn)容易出現(xiàn)內(nèi)存碎片的問(wèn)題,空間利用率低。
(5)迭代器:vector的迭代器是原生指針;list的迭代器對(duì)原生指針進(jìn)行了再一層的封裝。
(6)迭代器失效的問(wèn)題:vector再插入和刪除時(shí)都會(huì)導(dǎo)致迭代器失效,需要重新賦值;list只有再刪除時(shí)才會(huì)導(dǎo)致迭代器失效。
到此這篇關(guān)于C++ list常用接口和模擬實(shí)現(xiàn)實(shí)例代碼的文章就介紹到這了,更多相關(guān)C++ list常用接口內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C和C++中的基本數(shù)據(jù)類(lèi)型的大小及表示范圍詳解
這篇文章主要介紹了C和C++中的基本數(shù)據(jù)類(lèi)型的大小及表示范圍詳解,基本數(shù)據(jù)類(lèi)型有int、long、long long、float、double、char、string,正文有詳細(xì)介紹,歡迎參考2018-01-01
C語(yǔ)言 strcpy和memcpy區(qū)別詳細(xì)介紹
這篇文章主要介紹了C語(yǔ)言 strcpy和memcpy區(qū)別詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下2017-01-01
算法之排序算法的算法思想和使用場(chǎng)景總結(jié)
這篇文章主要介紹了算法之排序算法的算法思想和使用場(chǎng)景總結(jié),本文講解了插入排序、交換排序、選擇排序等幾大類(lèi)排序算法的特點(diǎn)、思想和使用場(chǎng)景,需要的朋友可以參考下2014-08-08

