欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

C++之list容器的解析與實(shí)現(xiàn)教程

 更新時(shí)間:2025年09月18日 09:19:41   作者:如意.759  
文章介紹了C++?STL中l(wèi)ist容器的特性:基于雙向循環(huán)鏈表,插入刪除高效但不支持排序,需用vector排序,迭代器模擬指針行為,刪除導(dǎo)致局部失效,對(duì)比vector,list適合動(dòng)態(tài)數(shù)據(jù),vector適合隨機(jī)訪問(wè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ī)訪問(wèn)元素,那么Vector或者數(shù)組可能是更佳的選擇。因?yàn)長(zhǎng)ist的順序訪問(wèn)性能相比之下會(huì)顯得有些力不從心。

  • 所以如果需要頻繁隨機(jī)的訪問(wèn)數(shù)據(jù),那么選擇vector容器
  • 如果需要頻繁插入刪除數(shù)據(jù),那么選擇list容器
  • 排序不要選擇list ?。?!可以先拷貝給vector排序后再拷貝回來(lái)

迭代器的介紹

迭代器可以按照功能、性質(zhì)兩大模塊分類

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

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

2、list的使用

list 中的接口比較多,此處類似,只需要掌握如何正確的使用,然后再去深入研究背后的原理,已達(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】

  1.  begin 與 end 為正向迭代器,對(duì)迭代器執(zhí)行++操作,迭代器向后移動(dòng)
  2. 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í)理解成類似于指針,迭代器失效即迭代器所指向的節(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在訪問(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)類,為了兼容迭代器的使用,我們需要對(duì)結(jié)點(diǎn)指針?lè)庋b一下即迭代器,最后就是實(shí)現(xiàn)功能函數(shù)的list 類了,我們可以加入一個(gè)頭結(jié)點(diǎn)方便插入刪除和兼容容器的左閉右開(kāi)。

結(jié)點(diǎn)類

// 結(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)類的結(jié)構(gòu)還是很簡(jiǎn)單的 結(jié)構(gòu)體里包含著數(shù)據(jù)和前后指針即可,因?yàn)橐啻卧L問(wèn)結(jié)點(diǎn)所以設(shè)置為 struct 較合適

list類

構(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)行初始化了

迭代器類

list的迭代器顯然不是指針!! 鏈表的物理地址并不是連續(xù)存儲(chǔ)的,指針的++ -- 操作是沒(méi)有意義的,所以我們需要實(shí)現(xiàn)一個(gè)用結(jié)點(diǎn)指針?lè)庋b的迭代器類,通過(guò)運(yùn)算符重載去模擬指針的行為

可以看到庫(kù)里所實(shí)現(xiàn)的迭代器類與我們所預(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)行封裝,因迭代器使用形式與指針完全相同,因此在自定義的類中必須實(shí)現(xiàn)以下方法:

(1)指針可以解引用,迭代器的類中必須重載 operator*()

(2)指針可以通過(guò)->訪問(wèn)其所指空間成員,迭代器類中必須重載 oprator->()

(3)指針可以++向后移動(dòng),迭代器類中必須重載 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為自定義類型的情況

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)容,只需要重載的 * 和 -> 返回類型加上const 修飾即可

但是注意到其他重載的運(yùn)算符都是一樣的,如果重新定義一個(gè)const 迭代器是否過(guò)于冗余呢?

觀察一下STL庫(kù)里是如何實(shí)現(xiàn)的

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

那么我們開(kāi)始優(yōu)化一下迭代器

將類模板修改一下

//reference 引用  pointer 指針
template<class T , class Ref ,class Ptr>

有差異的返回值也改變一下

Ref operator*() //現(xiàn)在返回const類型還是普通類型是通過(guò)編譯器推導(dǎo)的
{
	return _node->_data;
}

Ptr operator->()
{
	return &(_node->_data);
}

那么類實(shí)例化的時(shí)候傳入對(duì)應(yīng)參數(shù)編譯器就會(huì)推導(dǎo)出對(duì)應(yīng)的類:

typedef list_iterator<T,T&,T*> iterator;
typedef list_iterator<T,const T&,const T*> const_iterator;

測(cè)試一下

那么就實(shí)現(xiàn)成功迭代器類模板

3.2功能接口

迭代器接口

iterator begin()//第一個(gè)結(jié)點(diǎn)指針
{
	//iterator it(_head->_next);//構(gòu)造函數(shù)
	//return it; 優(yōu)化一下
	return _head->_next; //走的隱式類型轉(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)存泄漏?。?!

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)槭褂昧说?,刪除后會(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語(yǔ)言動(dòng)態(tài)內(nèi)存函數(shù)詳解

    C語(yǔ)言動(dòng)態(tài)內(nèi)存函數(shù)詳解

    這篇文章主要介紹了C語(yǔ)言動(dòng)態(tài)內(nèi)存函數(shù),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-09-09
  • C語(yǔ)言中#pragma?pack(1)的用法與注意點(diǎn)

    C語(yǔ)言中#pragma?pack(1)的用法與注意點(diǎn)

    #pragma用于指示編譯器完成一些特定的動(dòng)作,下面這篇文章主要給大家介紹了關(guān)于C語(yǔ)言中#pragma?pack(1)的用法與注意點(diǎn)的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-02-02
  • C++中的重載、覆蓋、隱藏介紹

    C++中的重載、覆蓋、隱藏介紹

    這篇文章主要介紹了C++中的重載、覆蓋、隱藏介紹,需要的朋友可以參考下
    2015-04-04
  • C++基于字符串實(shí)現(xiàn)大數(shù)相乘問(wèn)題的代碼詳解

    C++基于字符串實(shí)現(xiàn)大數(shù)相乘問(wèn)題的代碼詳解

    在實(shí)際編程中,我們經(jīng)常會(huì)遇到需要處理大整數(shù)的情況,由于編程語(yǔ)言中內(nèi)置整數(shù)類型有其表示范圍的限制,當(dāng)需要處理的整數(shù)超出這些范圍時(shí),就不能直接使用內(nèi)置類型進(jìn)行計(jì)算,所以本文給大家介紹了相關(guān)的解決方法,需要的朋友可以參考下
    2025-03-03
  • C++之list容器模擬實(shí)現(xiàn)方式

    C++之list容器模擬實(shí)現(xiàn)方式

    這篇文章主要介紹了C++之list容器模擬實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2023-02-02
  • C++中的throw關(guān)鍵字詳解

    C++中的throw關(guān)鍵字詳解

    throw關(guān)鍵字是在C語(yǔ)言中用來(lái)拋出異常的關(guān)鍵字,它通常與try和catch一起使用,用于在程序中發(fā)生錯(cuò)誤時(shí)進(jìn)行異常處理,當(dāng)遇到無(wú)法處理的錯(cuò)誤情況時(shí),我們可以使用throw關(guān)鍵字主動(dòng)拋出異常,所以本文給大家詳細(xì)的介紹一下C++中的throw關(guān)鍵字,需要的朋友可以參考下
    2023-09-09
  • C++實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(Map實(shí)現(xiàn))

    C++實(shí)現(xiàn)學(xué)生信息管理系統(tǒng)(Map實(shí)現(xiàn))

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)學(xué)生信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-06-06
  • 淺析C語(yǔ)言調(diào)試器GDB和LLDB的使用方法

    淺析C語(yǔ)言調(diào)試器GDB和LLDB的使用方法

    這篇文章主要介紹了C語(yǔ)言調(diào)試器GDB和LLDB的使用方法,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2019-12-12
  • 深入C++四種強(qiáng)制類型轉(zhuǎn)換的總結(jié)

    深入C++四種強(qiáng)制類型轉(zhuǎn)換的總結(jié)

    本篇文章是對(duì)C++中四種強(qiáng)制類型轉(zhuǎn)換進(jìn)行了詳細(xì)的分析介紹,需要的朋友參考下
    2013-05-05
  • 利用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲

    利用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲

    這篇文章主要介紹了用c++和easyx圖形庫(kù)做一個(gè)低配版掃雷游戲,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-01-01

最新評(píng)論