C++之list容器的解析與實(shí)現(xiàn)教程
1、list 介紹
List是C++標(biāo)準(zhǔn)模板庫(kù)(STL)中的一個(gè)成員,其本質(zhì)為帶頭雙向循環(huán)鏈表。不同于連續(xù)的、緊密排列的數(shù)組容器Vector,List容器的內(nèi)部是由雙向鏈表構(gòu)成的,使得它在插入和刪除操作上,就如同行云流水一般順暢,不需移動(dòng)其它元素。
List的結(jié)構(gòu)示意圖如下:環(huán)狀鏈表的尾是一個(gè)空節(jié)點(diǎn),符合“左閉右開(kāi)”區(qū)間。

List適合的場(chǎng)景是那些需要頻繁進(jìn)行插入和刪除操作的場(chǎng)合。
例如,如果你正在管理一個(gè)動(dòng)態(tài)變化的列表,如任務(wù)調(diào)度、人員排隊(duì)等場(chǎng)景,List的特性將大放異彩。但是如果你的應(yīng)用場(chǎng)景更多地需要隨機(jī)訪(fǎng)問(wèn)元素,那么Vector或者數(shù)組可能是更佳的選擇。因?yàn)長(zhǎng)ist的順序訪(fǎng)問(wèn)性能相比之下會(huì)顯得有些力不從心。
- 所以如果需要頻繁隨機(jī)的訪(fǎng)問(wèn)數(shù)據(jù),那么選擇vector容器
- 如果需要頻繁插入刪除數(shù)據(jù),那么選擇list容器
- 排序不要選擇list ?。?!可以先拷貝給vector排序后再拷貝回來(lái)
迭代器的介紹
迭代器可以按照功能、性質(zhì)兩大模塊分類(lèi)

迭代器的一些性質(zhì)(重載的運(yùn)算符)決定著算法庫(kù)里對(duì)算法的調(diào)用

而這些迭代器是子類(lèi)與父類(lèi)的繼承關(guān)系

2、list的使用
list 中的接口比較多,此處類(lèi)似,只需要掌握如何正確的使用,然后再去深入研究背后的原理,已達(dá)到可擴(kuò)展的能力。以下為list中一些常見(jiàn)的重要接口。
2.1 list 的構(gòu)造

void TestList1()
{
list<int> l1; // 構(gòu)造空的l1
list<int> l2(4, 100); // l2中放4個(gè)值為100的元素
list<int> l3(l2.begin(), l2.end()); // 用l2的[begin(), end())左閉右開(kāi)的區(qū)間構(gòu)造l3
list<int> l4(l3); // 用l3拷貝構(gòu)造l4
// 以數(shù)組為迭代器區(qū)間構(gòu)造l5
int array[] = { 16,2,77,29 };
list<int> l5(array, array + sizeof(array) / sizeof(int));
// 列表格式初始化C++11
list<int> l6{ 1,2,3,4,5 };
list<int> l7 = { 1,2,4,5,6,7,8 };
// 用迭代器方式打印l5中的元素
list<int>::iterator it = l5.begin();
while (it != l5.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// C++11范圍for的方式遍歷
for (auto& e : l5)
cout << e << " ";
cout << endl;
}2.2 iterator 的使用
關(guān)于 list 的迭代器可以姑且理解為指針,本質(zhì)是通過(guò)運(yùn)算符重載模擬指針的行為


【TIP】
- begin 與 end 為正向迭代器,對(duì)迭代器執(zhí)行++操作,迭代器向后移動(dòng)
- rbegin(end) 與 rend(begin) 為反向迭代器,對(duì)迭代器執(zhí)行++操作,迭代器向前移動(dòng)
void PrintList(const list<int>& l)
{
// 注意這里調(diào)用的是list的 begin() const,返回list的const_iterator對(duì)象
for (list<int>::const_iterator it = l.begin(); it != l.end(); ++it)
{
cout << *it << " ";
// *it = 10; 編譯不通過(guò)
}
cout << endl;
}
void TestList2()
{
int array[] = { 1, 2, 3, 4, 5, 6, };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
// 使用正向迭代器正向list中的元素
// list<int>::iterator it = l.begin(); // C++98中語(yǔ)法
auto it = l.begin(); // C++11之后推薦寫(xiě)法
while (it != l.end())
{
cout << *it << " ";
++it;
}
cout << endl;
// 使用反向迭代器逆向打印list中的元素
// list<int>::reverse_iterator rit = l.rbegin();
auto rit = l.rbegin();
while (rit != l.rend())
{
cout << *rit << " ";
++rit;
}
cout << endl;
//迭代器不支持 因?yàn)?list 迭代器是雙向 不支持 + -
/*l.insert(l.begin() + 3);*/
//想要實(shí)現(xiàn)第三個(gè)位置后插入一個(gè)數(shù)可以通過(guò)下面代碼
it = l.begin();
int k = 3;
while (k--)
{
it++;//支持++
}
l.insert(it, 40);
PrintList(l);
}2.3 list 的修改

struct A {
public:
A(int a1 = 1, int a2 = 1)
:_a1(a1)
, _a2(a2)
{
cout << "A(int a1=1,int a2=1)" << endl;
}
A(const A& aa)
:_a1(aa._a1)
, _a2(aa._a2)
{
cout << "A(const A& aa)" << endl;
}
private:
int _a1;
int _a2;
};
void TestList3()
{
int array[] = { 1, 2, 3 };
list<int> L(array, array + sizeof(array) / sizeof(array[0]));
// 在list的尾部插入4,頭部插入0
L.push_back(4);
L.push_front(0);
PrintList(L);
// 刪除list尾部節(jié)點(diǎn)和頭部節(jié)點(diǎn)
L.pop_back();
L.pop_front();
PrintList(L);
list<A>lt;
A aa1(1, 1);
lt.push_back(aa1);//有名對(duì)象尾插
lt.push_back(A(2, 2));//匿名對(duì)象尾插
//lt.push_back(3, 3);//不支持 push back只支持一個(gè)參數(shù)
lt.emplace_back(aa1);
lt.emplace_back(A(2, 2));
cout << endl;
// 前面的插入都是 構(gòu)造+拷貝構(gòu)造
// (3,3)的插入 并沒(méi)有拷貝構(gòu)造 性能上來(lái)說(shuō)優(yōu)于 push back
lt.emplace_back(3, 3);//支持 本質(zhì)支持直接傳構(gòu)造A對(duì)象的參數(shù)
}可以看到 emplace_back 可以支持直接傳構(gòu)造A的參數(shù) 省去了臨時(shí)變量的拷貝構(gòu)造
插入與刪除

// insert /erase
void TestList4()
{
int array1[] = { 1, 2, 3 };
list<int> L(array1, array1 + sizeof(array1) / sizeof(array1[0]));
// 獲取鏈表中第二個(gè)節(jié)點(diǎn)
auto pos = ++L.begin(); //pos 可以理解為下標(biāo)
cout << *pos << endl;
// 在pos前插入值為4的元素
L.insert(pos, 4);
PrintList(L);
// 在pos前插入6個(gè)值為5的元素
L.insert(pos, 6, 5);
PrintList(L);
// 在pos前插入[v.begin(), v.end)區(qū)間中的元素
vector<int> v{ 7, 8, 9 };
L.insert(pos, v.begin(), v.end());
PrintList(L);
// 刪除pos位置上的元素
L.erase(pos);
PrintList(L);
// 刪除list中[begin, end)區(qū)間中的元素,即刪除list中的所有元素
L.erase(L.begin(), L.end());
PrintList(L);
}

// resize/swap/clear
void TestList5()
{
// 用數(shù)組來(lái)構(gòu)造list
int array1[] = { 1, 2, 3 };
list<int> l1(array1, array1 + sizeof(array1) / sizeof(array1[0]));
PrintList(l1);
// 交換l1和l2中的元素
list<int> l2;
l1.swap(l2);
PrintList(l1);
PrintList(l2);
// 將l2中的元素清空
l2.clear();
cout << l2.size() << endl;
}2.4一些特殊接口

reverse 逆置鏈表 sort 排序 (算法庫(kù)里沒(méi)有l(wèi)ist的排序(迭代器不匹配))

void test_list4()//容器的 逆置與排序
{
list<int>lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
print_list(lt);
lt.reverse();
print_list(lt);
reverse(lt.begin(), lt.end());
print_list(lt);
reverse(++lt.begin(), --lt.end());
print_list(lt);
//算法庫(kù)里的排序和容器的排序都是默認(rèn)升序
lt.sort();
print_list(lt);
//想要降序 則需要 仿函數(shù)
//less<int>ls;//升序
//greater<int>gt;//降序
//還可以結(jié)合匿名對(duì)象使用
lt.sort(greater<int>());
print_list(lt);
}merge 合并 與 unique 去重(前提均有序鏈表)
void test_list5() // merge有序list合并 unique 有序list去除重復(fù)數(shù)據(jù)
{
std::list<double> first, second;
first.push_back(3.1);
first.push_back(2.2);
first.push_back(2.9);
second.push_back(3.7);
second.push_back(7.1);
second.push_back(1.4);
first.sort();
second.sort();
//將 second和first合并 second置為空 前提兩list有序
first.merge(second);
print_list(first);
print_list(second);
list<int>lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(2);
lt.push_back(3);
lt.push_back(3);
lt.push_back(4);
lt.push_back(1);
lt.push_back(1);
print_list(lt);
lt.sort();
//有序的數(shù)據(jù)去掉重復(fù)的數(shù)據(jù)
lt.unique();
print_list(lt);
}
剪切拼接 splice 的妙用
void test_list6()
{
//把一個(gè)鏈表結(jié)點(diǎn)轉(zhuǎn)移給另一個(gè)鏈表
std::list<int> mylist1, mylist2;
std::list<int>::iterator it;
// set some initial values:
for (int i = 1; i <= 4; ++i)
mylist1.push_back(i); // mylist1: 1 2 3 4
for (int i = 1; i <= 3; ++i)
mylist2.push_back(i * 10); // mylist2: 10 20 30
it = mylist1.begin();
++it; // points to 2
mylist1.splice(it, mylist2); // mylist1: 1 10 20 30 2 3 4
// mylist2 (empty)
// "it" still points to 2 (the 5th element)
print_list(mylist1);
print_list(mylist2);// splice 剪切功能
//splice 還可以調(diào)整當(dāng)前l(fā)ist的順序
list<int>lt;
lt.push_back(1);
lt.push_back(2);
lt.push_back(3);
lt.push_back(4);
lt.push_back(5);
print_list(lt);
int x = 0;
cin >> x;
it = find(lt.begin(), lt.end(), x);
if (it != lt.end())
{
// 調(diào)整當(dāng)前鏈表的元素順序 將 it指向元素 調(diào)整到 begin
lt.splice(lt.begin(), lt, it);
// //迭代器區(qū)間
//lt.splice(lt.begin(), lt, it,lt.end());
}
print_list(lt);
}
2.5 迭代器失效問(wèn)題
前面說(shuō)過(guò),此處可將迭代器暫時(shí)理解成類(lèi)似于指針,迭代器失效即迭代器所指向的節(jié)點(diǎn)的無(wú)效,即該節(jié)點(diǎn)被刪除了。
因?yàn)閘ist的底層結(jié)構(gòu)為帶頭結(jié)點(diǎn)的雙向循環(huán)鏈表,因此在 list 中進(jìn)行插入時(shí)是不會(huì)導(dǎo)致 list 的迭代器失效的,只有在刪除時(shí)才會(huì)失效,并且失效的只是指向被刪除節(jié)點(diǎn)的迭代器,其他迭代器不會(huì)受到影響。
這在 vector 是不成立的,因?yàn)?vector 的插入操作有元素移動(dòng)可能導(dǎo)致重新配置空間,導(dǎo)致原有的迭代器全部失效。
void TestListIterator1()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
// erase()函數(shù)執(zhí)行后,it所指向的節(jié)點(diǎn)已被刪除,因此it無(wú)效,在下一次使用it時(shí),必須先給其賦值
l.erase(it);
++it;
}
}- VS2022 對(duì)于迭代器失效有著嚴(yán)格的監(jiān)控機(jī)制直接報(bào)錯(cuò)

// 改正
void TestListIterator()
{
int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
list<int> l(array, array + sizeof(array) / sizeof(array[0]));
auto it = l.begin();
while (it != l.end())
{
l.erase(it++); //it = l.erase(it); erase 返回下一個(gè)位置的迭代器
}
}- 這里的 erase 會(huì)返回下一個(gè)結(jié)點(diǎn)的位置



對(duì)于 erase(it++); 可以理解為 先it++了再執(zhí)行erase 代碼塊里的內(nèi)容
3、實(shí)現(xiàn)list
list 容器的底層是一個(gè)帶頭雙向循環(huán)鏈表
- 雙向鏈表的意思是:每個(gè)節(jié)點(diǎn)不僅存儲(chǔ)著數(shù)據(jù),還包含兩個(gè)指針,分別指向前一個(gè)節(jié)點(diǎn)和后一個(gè)節(jié)點(diǎn)。這使得 list 可以在常數(shù)時(shí)間內(nèi)進(jìn)行雙向遍歷和插入、刪除操作。
庫(kù)里的節(jié)點(diǎn)結(jié)構(gòu)大致如下:

頭節(jié)點(diǎn)和尾節(jié)點(diǎn)會(huì)分別指向 nullptr ,這表明鏈表的邊界。
這種雙向鏈表的結(jié)構(gòu)給予了 list 靈動(dòng)的特性:它能夠輕松地在任意位置增加或移除元素,而這種操作幾乎是與容器大小無(wú)關(guān)的,體現(xiàn)了時(shí)間復(fù)雜度上的優(yōu)勢(shì)。但這種優(yōu)勢(shì)的代價(jià)是,與數(shù)組相比,List在訪(fǎng)問(wèn)元素時(shí)的速度會(huì)較慢,因?yàn)樗枰獜念^開(kāi)始遍歷。這也決定了list的更適合頻繁插入的動(dòng)態(tài)數(shù)據(jù)。
3.1底層結(jié)構(gòu)
根據(jù)對(duì)鏈表的理解,我們需要封裝一個(gè)結(jié)點(diǎn)類(lèi),為了兼容迭代器的使用,我們需要對(duì)結(jié)點(diǎn)指針?lè)庋b一下即迭代器,最后就是實(shí)現(xiàn)功能函數(shù)的list 類(lèi)了,我們可以加入一個(gè)頭結(jié)點(diǎn)方便插入刪除和兼容容器的左閉右開(kāi)。
結(jié)點(diǎn)類(lèi)
// 結(jié)點(diǎn)結(jié)構(gòu)體
template<class T>
struct list_node //默認(rèn)公有 struct
{
T _data;
list_node<T>* _prev;
list_node<T>* _next;
list_node(const T& data=T())
:_data(data)
,_prev(nullptr)
,_next(nullptr)
{}
~list_node()
{
_next = nullptr;
_prev = nullptr;
}
};結(jié)點(diǎn)類(lèi)的結(jié)構(gòu)還是很簡(jiǎn)單的 結(jié)構(gòu)體里包含著數(shù)據(jù)和前后指針即可,因?yàn)橐啻卧L(fǎng)問(wèn)結(jié)點(diǎn)所以設(shè)置為 struct 較合適
list類(lèi)
構(gòu)造函數(shù)需要?jiǎng)?chuàng)建一個(gè)頭結(jié)點(diǎn),因?yàn)槲覀円獎(jiǎng)?chuàng)建雙向循環(huán)鏈表,所以頭尾都要指向頭結(jié)點(diǎn)本身。 _size統(tǒng)計(jì)元素個(gè)數(shù)。
template<class T>
class list
{
typedef list_node<T> Node;
public:
typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&, const T*> const_iterator;
void empty_init()
{
//_head = new Node;//這里寫(xiě)了 Node 帶參數(shù)構(gòu)造 所以報(bào)錯(cuò) 需要匿名參數(shù)
_head = new Node(T());
_head->_next = _head;
_head->_prev = _head;
_size = 0;
}
list()
{
empty_init();
}
list(initializer_list<T>il)
{
empty_init();
for (auto& e : il)
{
push_back(e);
}
_size = il.size();
}
private:
Node* _head;
size_t _size;
};這里的 list(initializer_list<T>il) 是C++11出現(xiàn)的語(yǔ)法,就可以像數(shù)組一樣進(jìn)行初始化了
迭代器類(lèi)
list的迭代器顯然不是指針!! 鏈表的物理地址并不是連續(xù)存儲(chǔ)的,指針的++ -- 操作是沒(méi)有意義的,所以我們需要實(shí)現(xiàn)一個(gè)用結(jié)點(diǎn)指針?lè)庋b的迭代器類(lèi),通過(guò)運(yùn)算符重載去模擬指針的行為

可以看到庫(kù)里所實(shí)現(xiàn)的迭代器類(lèi)與我們所預(yù)想的一致!
如何實(shí)現(xiàn)++ 到下一個(gè)結(jié)點(diǎn)呢??

通過(guò) 重載 ++運(yùn)算符 獲取地址返回下一個(gè)結(jié)點(diǎn)即可
迭代器實(shí)現(xiàn):
由于STL list是一個(gè)雙向鏈表,迭代器必須具備遷移,后移的能力,所以 list 提供的是 Birdirectional Iterator(雙向)
List 的迭代器
迭代器有兩種實(shí)現(xiàn)方式,具體應(yīng)根據(jù)容器底層數(shù)據(jù)結(jié)構(gòu)實(shí)現(xiàn):
1. 原生態(tài)指針,比如:vector
2. 將原生態(tài)指針進(jìn)行封裝,因迭代器使用形式與指針完全相同,因此在自定義的類(lèi)中必須實(shí)現(xiàn)以下方法:
(1)指針可以解引用,迭代器的類(lèi)中必須重載 operator*()
(2)指針可以通過(guò)->訪(fǎng)問(wèn)其所指空間成員,迭代器類(lèi)中必須重載 oprator->()
(3)指針可以++向后移動(dòng),迭代器類(lèi)中必須重載 operator++() 與 operator++(int)
至于 operator--() / operator--(int) 是否需要重載,根據(jù)具體的結(jié)構(gòu)來(lái)抉擇,雙向鏈表可以向前移動(dòng),所以需要重載,如果是forward_list(單向)就不需要重載--
(4)迭代器需要進(jìn)行是否相等的比較,因此還需要重載 operator==() 與 operator!=()
template<class T>
struct list_iterator
{
typedef list_node<T> Node;
typedef list_iterator<T> Self;
Node* _node;//指針成員變量
list_iterator(Node* node)
:_node(node)
{}
T& operator*()
{
return _node->_data;
}
T* operator->()
{
return &(_node->_data);
}
//操作符重載 前置++ 與 后置++的區(qū)別是參數(shù)是否帶(int)
// ++it
Self& operator++()
{
_node = _node->_next;
return *this;
}
// it++
Self& operator++(int x)
{
Self tmp(*this);//淺拷貝
_node = _node->_next;
return tmp;
}
//--it
Self& operator--()
{
_node = _node->_prev;
return *this;
}
//it--
Self& operator--(int x)
{
Self tmp(*this);
_node = _node->_prev;
return tmp;
}
bool operator!=(const Self& s)const { return _node != s._node; }
bool operator==(const Self& s)const { return _node == s._node; }
};有必要注意的是 重載 -> 是為了適配T為自定義類(lèi)型的情況
list<AA>lta;
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
lta.push_back(AA());
auto ita = lta.begin();
while (ita != lta.end())
{
//cout << *ita << ' '; //編譯不通過(guò) 結(jié)構(gòu)體并沒(méi)有重載流插入
//cout << (*ita)._a1 << ' ';
//cout << ita->_a1 << ' '; //為了可讀性省去了一個(gè)箭頭
cout << ita.operator->()->_a1 << ' ';
++ita;
}調(diào)用了 operator ->后 AA* 結(jié)構(gòu)體指針再用->調(diào)用了_a1
這樣迭代器大致實(shí)現(xiàn)了應(yīng)該有的功能,我們還需要實(shí)現(xiàn)一個(gè)const 迭代器,如何封裝const 迭代器呢?不能修改內(nèi)容,只需要重載的 * 和 -> 返回類(lèi)型加上const 修飾即可

但是注意到其他重載的運(yùn)算符都是一樣的,如果重新定義一個(gè)const 迭代器是否過(guò)于冗余呢?
觀(guān)察一下STL庫(kù)里是如何實(shí)現(xiàn)的

顯然并不是冗余的復(fù)制一份迭代器,而是同一個(gè)類(lèi)模板實(shí)現(xiàn)的,實(shí)例化兩個(gè)不同的類(lèi),本質(zhì)上與冗余的寫(xiě)法是一個(gè)道理

那么我們開(kāi)始優(yōu)化一下迭代器
將類(lèi)模板修改一下
//reference 引用 pointer 指針 template<class T , class Ref ,class Ptr>
有差異的返回值也改變一下
Ref operator*() //現(xiàn)在返回const類(lèi)型還是普通類(lèi)型是通過(guò)編譯器推導(dǎo)的
{
return _node->_data;
}
Ptr operator->()
{
return &(_node->_data);
}那么類(lèi)實(shí)例化的時(shí)候傳入對(duì)應(yīng)參數(shù)編譯器就會(huì)推導(dǎo)出對(duì)應(yīng)的類(lèi):
typedef list_iterator<T,T&,T*> iterator; typedef list_iterator<T,const T&,const T*> const_iterator;
測(cè)試一下

那么就實(shí)現(xiàn)成功迭代器類(lèi)模板
3.2功能接口
迭代器接口
iterator begin()//第一個(gè)結(jié)點(diǎn)指針
{
//iterator it(_head->_next);//構(gòu)造函數(shù)
//return it; 優(yōu)化一下
return _head->_next; //走的隱式類(lèi)型轉(zhuǎn)換
}
// const 修飾begin 可以兼容拷貝構(gòu)造 否則出現(xiàn)權(quán)限放大 這是不允許的
const_iterator begin()const //前面的const 迭代器是指向元素或引用不可以改變內(nèi)容 后面的const是修飾this 對(duì)象不可以改變
{ // 隱含的this指針由 list * const this 變?yōu)?const list* const this 前者是指針不可以更改指向
return _head->_next;
}
const_iterator cbegin()const
{
return _head->_next;
}
iterator end()
{
return _head; // 頭節(jié)點(diǎn) 左閉右開(kāi)
}
const_iterator end()const
{
return _head;
}
const_iterator cend()const
{
return _head;
}const 修飾begin 是為了防止權(quán)限放大的錯(cuò)誤出現(xiàn)

因?yàn)榭截悩?gòu)造中參數(shù)是 const 對(duì)象
插入刪除
由于雙向循環(huán),插入刪除相對(duì)比較簡(jiǎn)單,就是注意鏈接的順序性即可
void push_back(const T& x)
{
//Node* newnode = new Node(x); //沒(méi)有寫(xiě)Node 構(gòu)造報(bào)錯(cuò)
//_head->_prev->_next = newnode;
//newnode->_next = _head;
//newnode->_prev = _head->_prev;
//_head->_prev = newnode;
//++_size;
insert(end(), x);//復(fù)用 insert
}
void push_front(const T& x)
{
insert(begin(), x);
}
iterator insert(iterator pos, const T& x)
{
Node* cur = pos._node;
Node* prev = cur->_prev;
Node* newnode = new Node(x);
// prev newnode cur
newnode->_next = cur;
cur->_prev = newnode;
newnode->_prev = prev;
prev->_next = newnode;
++_size;
return newnode;
}實(shí)現(xiàn)了 insert 我們可以根據(jù)迭代器復(fù)用 insert 實(shí)現(xiàn)尾插和頭插
TIp:新節(jié)點(diǎn)將位于插入點(diǎn)的前一個(gè)位置這是STL對(duì)于“插入操作”的標(biāo)準(zhǔn)規(guī)范。
刪除操作,與 insert 一樣使用指針操作,來(lái)達(dá)到刪除的效果。注意要對(duì)刪除的節(jié)點(diǎn)進(jìn)行釋放空間操作(delete),不然會(huì)發(fā)生內(nèi)存泄漏?。。?/span>
void pop_back()
{
erase(--end());
}
void pop_front()
{
erase(begin());
}
iterator erase(iterator pos) //防止迭代器失效問(wèn)題
{
assert(pos != end());//不可以刪除頭結(jié)點(diǎn)
Node* prev = pos._node->_prev;;
Node* next = pos._node->_next;
prev->_next = next;
next->_prev = prev;
delete pos._node;
--_size;
return next;
}
需要注意的是,任意位置刪除因?yàn)槭褂昧说鳎瑒h除后會(huì)造成迭代器失效,所以需要更新迭代器,返回被刪除節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)的迭代器即可。
拷貝賦值析構(gòu)函數(shù)
//lt1(lt)
list( const list<T>& lt)
{
empty_init();
for (auto& e : lt)
{
push_back(e);
}
}
void swap(list<T>& lt)
{
std::swap(_head, lt._head);
std::swap(_size, lt._size);
}
//lt1=lt3
list<T>& operator=(list<T> lt)
{
swap(lt);
return *this;
}
~list()
{
clear();
delete _head;
_head = nullptr;
}
void clear()
{
auto it = begin();
while (it!=end())
{
//erase(it++);//可以這樣寫(xiě) 可以理解為 先進(jìn)行了 it++后再 執(zhí)行 erase 代碼塊里的內(nèi)容
it=erase(it);
}
_size = 0;
}拷貝構(gòu)造我們只需要范圍 for 遍歷依次尾插即可,這里空初始化是為了創(chuàng)捷頭結(jié)點(diǎn),賦值重載只需要交換頭節(jié)點(diǎn)就可以
4、list與vector 區(qū)別對(duì)比
vector 與 list 都是STL中非常重要的序列式容器,由于兩個(gè)容器的底層結(jié)構(gòu)不同,導(dǎo)致其特性以及應(yīng)用場(chǎng)景不同,其主要不同如下:

總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
一步步從底層入手搞定C++引用與內(nèi)聯(lián)函數(shù)
內(nèi)聯(lián)函數(shù)是代碼插入到調(diào)用者代碼處的函數(shù),內(nèi)聯(lián)函數(shù)通過(guò)避免被調(diào)用的開(kāi)銷(xiāo)來(lái)提高執(zhí)行效率,下面這篇文章主要給大家介紹了關(guān)于如何從底層入手搞定C++引用與內(nèi)聯(lián)函數(shù)的相關(guān)資料,需要的朋友可以參考下2023-03-03
淺談C++/C關(guān)于#define的那些奇奇怪怪的用法
本文主要介紹了C++/C關(guān)于#define的那些奇奇怪怪的用法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07
COLORREF,COLOR,RGB,CString的轉(zhuǎn)化總結(jié)分析
實(shí)際的軟件開(kāi)發(fā)過(guò)程中,常需要用到非.net平臺(tái)的代碼。這時(shí)候就可能碰到ColorRef(也就是以int類(lèi)型代表的顏色值或是以DWORD值表示的顏色)。這跟.net平臺(tái)下的顏色的相互轉(zhuǎn)換MS并沒(méi)有直接實(shí)現(xiàn)2013-09-09
為什么說(shuō)C語(yǔ)言是永不過(guò)時(shí)的語(yǔ)言
時(shí)隔5年,C語(yǔ)言再次領(lǐng)先Java,榮登TIOBE編程語(yǔ)言排行榜第一,那么C語(yǔ)言為何不會(huì)過(guò)時(shí)?你需要掌握多少種語(yǔ)言呢,感興趣的朋友通過(guò)本文一起學(xué)習(xí)下吧2020-11-11
快速掌握VC6.0中各種宏注釋?xiě)?yīng)用(附圖)
為了方便別人或自己閱讀自己的程序,注釋是堅(jiān)決不可少的,一個(gè)漂亮的程序,不是在于你應(yīng)用的技術(shù)多么高深,而是能夠把高深的技術(shù)描述的清楚易懂2013-01-01
C++實(shí)現(xiàn)LeetCode(119.楊輝三角之二)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(119.楊輝三角之二),本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07
opencv利用霍夫變換檢測(cè)直線(xiàn)進(jìn)行圖片校正
這篇文章主要為大家詳細(xì)介紹了opencv利用霍夫變換檢測(cè)直線(xiàn)對(duì)圖片進(jìn)行校正,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12
詳解C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配
內(nèi)存分配 (Memory Allocation) 是指為計(jì)算機(jī)程序或服務(wù)分配物理內(nèi)存空間或虛擬內(nèi)存空間的一個(gè)過(guò)程,本文主要介紹了C++的靜態(tài)內(nèi)存分配與動(dòng)態(tài)內(nèi)存分配,感興趣的同學(xué)可以參考閱讀2023-06-06

