国产 无码 综合区,色欲AV无码国产永久播放,无码天堂亚洲国产AV,国产日韩欧美女同一区二区

【C++】list基本接口+手撕 list(詳解迭代器)

這篇具有很好參考價值的文章主要介紹了【C++】list基本接口+手撕 list(詳解迭代器)。希望對大家有所幫助。如果存在錯誤或未考慮完全的地方,請大家不吝賜教,您也可以點擊"舉報違法"按鈕提交疑問。

父母就像迭代器,封裝了他們的脆弱......?

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

手撕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

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

構(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

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

增刪查改:?

函數(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接口??難道說庫中的封裝性不好?效率不高?

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

?我們先使用庫中自己的sort函數(shù):

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

我們使用算法庫中的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;
}

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

報錯了(意外之中,如果不報錯我還寫這個知識點干啥 doge) 報錯原因說沒有-迭代器

讓我們看看sort源碼~

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

這一切的一切都是因為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);
}

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

測試二: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);
}

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

?可以看到,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

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

為什么在 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ù)的

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl?


迭代器還可以分成普通迭代器和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);
		}

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

?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的位置

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

同時這個問題還可以延伸出另一個問題:為什么迭代器訪問元素的時候要這樣寫?

?在vector中,物理地址是連續(xù)的,這么寫還情有可原,分析過list的begin和end之后,你還敢這么寫嗎??

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

?直接就報錯了,所以正確的應該是!=,而不是 <

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沒有類型:

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

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 != &lt)
		//	{
		//		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;
	};
}

?

【C++】list基本接口+手撕 list(詳解迭代器),小陽c++專欄,c++,stl

完結(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)!

本文來自互聯(lián)網(wǎng)用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權(quán),不承擔相關(guān)法律責任。如若轉(zhuǎn)載,請注明出處: 如若內(nèi)容造成侵權(quán)/違法違規(guī)/事實不符,請點擊違法舉報進行投訴反饋,一經(jīng)查實,立即刪除!

領(lǐng)支付寶紅包贊助服務器費用

相關(guān)文章

  • C++初階:適合新手的手撕list(模擬實現(xiàn)list)

    C++初階:適合新手的手撕list(模擬實現(xiàn)list)

    上次講了常用的接口:今天就來進行模擬實現(xiàn)啦 list.h頭文件:包含類的全部(函數(shù)的聲明與定義) reverseIterator.h文件:進行反向迭代器的實現(xiàn) test.cpp源文件:進行調(diào)用test函數(shù),測試和完善功能 基本結(jié)構(gòu): ListNode 結(jié)構(gòu)體: 定義了鏈表的節(jié)點結(jié)構(gòu),包含了三個成員變量:前驅(qū)指針

    2024年02月20日
    瀏覽(29)
  • 【c++】list迭代器失效問題

    【c++】list迭代器失效問題

    目錄 一、list iterator的使用 二、list的迭代器失效 ? ? ? ? 對于list的迭代器的用法,可以將它看做一個指針(實際要更加復雜)來使用,該指針指向list中的一個節(jié)點。 ? ? ? ? 【注意】 ? ? ? ? (1)begin和end為正向迭代器,對迭代器執(zhí)行++操作,迭代器向后移動 ? ? ? ? (2)r

    2024年01月23日
    瀏覽(23)
  • 【C++】詳解map和set基本接口及使用

    【C++】詳解map和set基本接口及使用

    關(guān)聯(lián)式容器也是用來存儲數(shù)據(jù)的,但與序列式容器不同的是,關(guān)聯(lián)式容器里面存儲的是 key, value 結(jié)構(gòu)的鍵值對,因此**在數(shù)據(jù)檢索時比序列式容器效率更高**。 鍵值對是用來表示 具有一一對應關(guān)系的一種結(jié)構(gòu) ,該結(jié)構(gòu)中一般只包含兩個成員變量 – key 和 value;其中 key 代表鍵

    2024年02月08日
    瀏覽(22)
  • 【C++進階(四)】STL大法--list深度剖析&list迭代器問題探討

    【C++進階(四)】STL大法--list深度剖析&list迭代器問題探討

    ??博主CSDN主頁:杭電碼農(nóng)-NEO?? ? ?專欄分類:C++從入門到精通? ? ??代碼倉庫:NEO的學習日記?? ? ??關(guān)注我??帶你學習C++ ? ???? 本質(zhì)重點: 本章重點講解list的接口函數(shù)的熟悉 并且講解list迭代器失效的特性 最后講解迭代器的功能分類以及 算法庫函數(shù)中誰能用誰不能

    2024年02月09日
    瀏覽(56)
  • C++:模擬實現(xiàn)list及迭代器類模板優(yōu)化方法

    C++:模擬實現(xiàn)list及迭代器類模板優(yōu)化方法

    本篇模擬實現(xiàn)簡單的list和一些其他注意的點 如下所示是利用拷貝構(gòu)造將一個鏈表中的數(shù)據(jù)挪動到另外一個鏈表中,構(gòu)造兩個相同的鏈表 lt作為形參,傳引用可以提高傳參的效率,同時應該加上const修飾來保證不會因為不小心修改了形參導致外部的實參也被修改,這樣的結(jié)果是

    2024年02月13日
    瀏覽(21)
  • C++:關(guān)于模擬實現(xiàn)vector和list中迭代器模塊的理解

    C++:關(guān)于模擬實現(xiàn)vector和list中迭代器模塊的理解

    本篇是關(guān)于 vector 和 list 的模擬實現(xiàn)中,關(guān)于迭代器模塊的更進一步理解,以及在前文的基礎(chǔ)上增加對于反向迭代器的實現(xiàn)和庫函數(shù)的對比等 本篇是寫于前面模擬實現(xiàn)的一段時間后,重新回頭看迭代器的實現(xiàn),尤其是在模板角度對 list 中迭代器封裝的部分進行解析,希望可以

    2024年02月07日
    瀏覽(23)
  • C++之std::list<string>::iterator迭代器應用實例(一百七十九)

    C++之std::list<string>::iterator迭代器應用實例(一百七十九)

    簡介: CSDN博客專家,專注Android/Linux系統(tǒng),分享多mic語音方案、音視頻、編解碼等技術(shù),與大家一起成長! 優(yōu)質(zhì)專欄: Audio工程師進階系列 【 原創(chuàng)干貨持續(xù)更新中…… 】?? 人生格言: 人生從來沒有捷徑,只有行動才是治療恐懼和懶惰的唯一良藥. 更多原創(chuàng),歡迎關(guān)注:An

    2024年02月12日
    瀏覽(27)
  • 【C++】反向迭代器的模擬實現(xiàn)通用(可運用于vector,string,list等模擬容器)

    【C++】反向迭代器的模擬實現(xiàn)通用(可運用于vector,string,list等模擬容器)

    ??博客主頁: 主頁 ??系列專欄: C++ ??感謝大家點贊??收藏?評論?? ??期待與大家一起進步! 我們要寫出一個通用的反向迭代器模擬而且在保證代碼簡介不繁瑣的的情況下,一定程度上使用我們自己模擬的已經(jīng)封裝好的iterator迭代器可以簡化許多步驟,首先我們要知

    2024年02月14日
    瀏覽(29)
  • 手撕學生管理系統(tǒng)超詳解——【c++】

    手撕學生管理系統(tǒng)超詳解——【c++】

    題目要求:設(shè)計一個學生成績管理程序,實現(xiàn)按班級完成對學生成績信息的錄入和修改,并用文件保存。 實現(xiàn)按班級輸出學生的成績單;實現(xiàn)按學號和姓名進行查詢,按平均成績進行排序功能。 問題描述 該程序的目標是提供一個簡單且易于使用的學生成績管理工具,以便教育

    2024年02月11日
    瀏覽(16)
  • C++迭代器(STL迭代器)iterator詳解

    C++迭代器(STL迭代器)iterator詳解

    要訪問順序容器和關(guān)聯(lián)容器中的元素,需要通過“迭代器(iterator)”進行。迭代器是一個變量,相當于容器和操縱容器的算法之間的中介。迭代器可以指向容器中的某個元素,通過迭代器就可以讀寫它指向的元素。從這一點上看,迭代器和指針類似。 迭代器按照定義方式分

    2024年02月03日
    瀏覽(22)

覺得文章有用就打賞一下文章作者

支付寶掃一掃打賞

博客贊助

微信掃一掃打賞

請作者喝杯咖啡吧~博客贊助

支付寶掃一掃領(lǐng)取紅包,優(yōu)惠每天領(lǐng)

二維碼1

領(lǐng)取紅包

二維碼2

領(lǐng)紅包