最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

C++深入探究list的模擬實現(xiàn)

 更新時間:2022年07月04日 08:38:14   作者:Hero?2021  
list相較于vector來說會顯得復(fù)雜,它的好處是在任意位置插入,刪除都是一個O(1)的時間復(fù)雜度,本文主要介紹了C++中List的模擬實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

示意圖:

迭代器

正向迭代器類

我們之前所理解的是:迭代器理解為像指針一樣的東西,但是在list中有些不同

// 迭代器邏輯
while(it!=l.end())
{
	*it; // 解引用取數(shù)據(jù)
	++it;// 自加到達(dá)下一個位置
}

我們可以來觀察一下STL源碼中大佬是怎么封裝的:

我們可以看到,只有一個成員,那就是一個結(jié)點的指針node,link_type又是一個自定義類型的指針,我們原生類型的指針在vector或者string中是可以直接typedef成為迭代器的,但是list底層是雙鏈表,數(shù)據(jù)結(jié)構(gòu)并非連續(xù)的,所以直接*it或者++it是不能夠完成迭代器的任務(wù)的,但是C++中支持對于自定義類型的運算符重載,我們可以對解引用和自加兩個運算符重載。

++it:就是當(dāng)前指針存放下一個結(jié)點的地址

*it:解引用當(dāng)前節(jié)點,取出值來

迭代器中,拷貝構(gòu)造、運算符賦值重載、析構(gòu)都不需要自己實現(xiàn),使用默認(rèn)生成的即可(即淺拷貝),因為迭代器是用自定義類型的指針封裝的,訪問修改鏈表,節(jié)點屬于鏈表,不屬于迭代器,所以不用管它。

我們在傳入const版本的list時,list是const對象,需要的是const_iterator,這里會出現(xiàn)錯誤,不能將const的list的迭代器傳給普通迭代器。如下所示例子:

void print_list(const list<int>& lt)
{
	// lt.begin()是const迭代器(只可讀)
	// it是普通迭代器(可讀可寫)
	list<int>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
}

現(xiàn)在我們?nèi)绾螌崿F(xiàn)一個const的迭代器呢?

意思就是只可以讀不能夠?qū)???梢?code>++,- -,*解引用,但是解引用時不能修改數(shù)據(jù)。

可以想到這種寫法:

const T& operator*()const
{
	return _node->_data;
}
T& operator*()
{
	return _node->_data;
}

但是并不是迭代器是const的,而是我們傳入的list容器是const版本的。

我們可以將寫一個const_iterator 的類版本,這樣普通迭代器和const迭代器就不是一個類型了,是兩個不同的類型了,這樣會造成代碼冗余。

template<class T>
struct __const_list_iterator
{
	//...
	// __list_iterator全部替換為__const_list_iterator
};

優(yōu)化:

增加一個模板參數(shù)class Ref

這樣第一個模板參數(shù)都是T,我們可以根據(jù)傳入的第二個參數(shù)來推出時T&還是const T&,本來我們是要實現(xiàn)兩個類的,現(xiàn)在只需要增加一個模板參數(shù)即可,這里體現(xiàn)出了C++泛型的優(yōu)勢!

迭代器我們說,它是像指針一樣的東西,如果它是指向的一個結(jié)構(gòu)體,需要用它的成員變量,我們還需要重載->箭頭

struct Date {
	int _year;
	int _month;
	int _day;
	Date(int year = 0, int month = 0, int day = 0)
	//這里要給默認(rèn)參數(shù),因為需要構(gòu)建一個哨兵位頭結(jié)點
		:_year(year)
		, _month(month)
		, _day(day)
	{}
};
void test_list2()
{
	list<Date> lt;
	lt.push_back(Date(2022, 1, 1));
	lt.push_back(Date(2022, 1, 2));
	lt.push_back(Date(2022, 1, 3));
	lt.push_back(Date(2022, 1, 4));
	// 現(xiàn)在來遍歷日期類
	list<Date>::iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << (*it)._year << "/" << (*it)._month << "/" << (*it)._day << endl;
		++it;
	}
	cout << endl;
}

這里的*解引用然后再去.,我們可以重載->,讓他可以去調(diào)用結(jié)構(gòu)體的成員,這樣更加快捷高效方便。

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

進(jìn)一步解釋:

*it調(diào)用operator*,返回一個結(jié)點對象,對象再.操作,拿到數(shù)據(jù)

it->調(diào)用operator->,返回對象的指針,(這里返回的是原生指針)再通過->調(diào)用用結(jié)構(gòu)體成員,這里實際上應(yīng)該是it->->_year,但是這樣寫,可讀性很差,所以編譯器做了一個優(yōu)化,省略了一個->,所以所有的類型只要想要重載->,都會這樣優(yōu)化省略一個->

這里又會衍生出一個問題,那就是如果使用const_iterator,使用->也會修改數(shù)據(jù),所以再增加一個模板參數(shù)

// 正向迭代器類
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef	ListNode<T> Node;
	typedef __list_iterator<T, Ref, Ptr> self;// 再次typedef,方便后續(xù)的修改
	Node* _node;
	__list_iterator(Node* x)// 迭代器的實質(zhì),就是自定義類型的指針
		:_node(x)
	{}
	// ++it 返回++之后的引用對象
	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	// it++ 返回++之前的對象
	self operator++(int)
	{
		self  tmp(this);
		_node = _node->_next;
		return tmp;
	}
	// --it 
	self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	// it-- 
	self operator--(int)
	{
		self tmp(this);
		_node = _node->_prev;
		return tmp;
	}
	// 返回引用,可讀可寫
	Ref operator*()
	{
		return _node->_data;
	}
	// 返回對象的指針
	Ptr operator->()
	{
		return &(_node->_data);
	}
	bool operator!=(const self& it)const
	{
		return _node != it._node;
	}
	bool operator==(const self& it)const
	{
		return _node == it._node;
	}
};

反向迭代器類

實質(zhì):對于正向迭代器的一種封裝

反向迭代器跟正想迭代器區(qū)別就是++,- -的方向是相反的

所以反向迭代器封裝正向迭代器即可,重載控制++,- -的方向

#pragma once
// reverse_iterator.h
namespace sjj 
{
	template <class Iterator, class Ref, class Ptr>
	class reverse_iterator
	{
		typedef reverse_iterator<Iterator,Ref,Ptr> self;
	public:
		reverse_iterator(Iterator it)
			:_it(it)
		{}
		// 比較巧妙,解引用取的是當(dāng)前位置的前一個位置的數(shù)據(jù)
		// operator*取前一個位置, 主要就是為了讓反向迭代器開始和結(jié)束跟正向迭代器對稱
		Ref operator *()
		{
			Iterator tmp = _it;
			return *--tmp;
		}
		Ptr operator->()
		{
			return &(operator*());
		}
		self& operator++()
		{
			--_it;
			return *this;
		}
		self operator++(int)
		{
			self tmp = *this;
			--_it;
			return tmp;
		}
		self& operator--()
		{
			++_it;
			return *this;
		}
		self operator--(int)
		{
			self tmp = *this;
			++_it;
			return tmp;
		}
		bool operator!=(const self& rit)
		{
			return _it != rit._it;
		}
		bool operator==(const self& rit)
		{
			return _it == rit._it;
		}
	private:
		Iterator _it;
	};
}

push_back尾插函數(shù)

void push_back(const T& x)
{
	// 先找尾記錄
	/*Node* tail = _head->_prev;
	Node* newnode = new Node(x);
	tail->_next = newnode;
	newnode->_prev = tail;
	_head->_prev = newnode;
	newnode->_next = _head;
	tail = tail->_next;*/
	// 復(fù)用insert函數(shù)
	insert(end(), x);
}

push_front頭插函數(shù)

// 頭插
void push_front(const T& x)
{
	// 復(fù)用insert函數(shù)
	insert(begin(), x);
}

insert插入函數(shù)

// 在pos位置前插入
iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* newnode = new Node(x);
	prev->_next = newnode;
	newnode->_prev = prev;
	newnode->_next = cur;
	cur->_prev = newnode;
	return iterator(newnode);// 返回新插入結(jié)點位置的迭代器
}

注意:這里list的insert函數(shù),pos位置的迭代器不會失效,因為pos指向的位置不會改變,vector中迭代器失效的原因是因為挪動數(shù)據(jù),導(dǎo)致指向的位置的數(shù)據(jù)發(fā)生變化。

erase刪除函數(shù)

iterator erase(iterator pos)
{
	assert(pos != end());//不能將哨兵位的頭結(jié)點給刪除了
	Node* prev = pos._node->_prev;
	Node* next = pos._node->_next;
	delete pos._node;
	prev->_next = next;
	next->_prev = prev;
	return iterator(next);// 返回pos位置的下一個位置的迭代器
}

注意:這里的pos位置的迭代器一定會失效,因為都已經(jīng)將結(jié)點給刪除了。

pop_front函數(shù)

// 復(fù)用erase函數(shù)
void pop_front()
{
	erase(begin());
}

pop_back函數(shù)

// 復(fù)用erase函數(shù)
void pop_back()
{
	erase(--end());
}

構(gòu)造函數(shù)

list()
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
}
// 函數(shù)模板,用迭代器區(qū)間進(jìn)行初始化
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
list(size_t n, const T& val = T())
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

注意:

這兩個構(gòu)造函數(shù)一起使用可能會存在問題,填充版本和構(gòu)造器版本可能會存在沖突,如下例子:

struct Date {
	int _year;
	int _month;
	int _day;
	Date(int year = 0, int month = 0, int day = 0)//這里要給默認(rèn)參數(shù),因為有一個哨兵位頭結(jié)點需要初始化
		:_year(year)
		, _month(month)
		, _day(day)
	{}
};
void test_list4()
{
	list<Date> lt1(5, Date(2022, 9, 9));
	for (auto e : lt1)
	{
		cout << e._year << "/" << e._month << "/" << e._day << endl;
	}
	cout << endl;

	list<int> lt2(5, 1);
	for (auto e : lt2)
	{
		cout << e << " ";
	}
	cout << endl;
}

對于這兩個:在實例化時會調(diào)用更加匹配的構(gòu)造函數(shù)初始化

list lt1(5, Date(2022, 9, 9))它會正常調(diào)用list(size_t n, const T& val = T())

list lt2(5, 1)而它會將5和1推演成兩個int,進(jìn)而去匹配這個迭代器版本的構(gòu)造函數(shù)

template < class InputIterator> list(InputIterator first, InputIterator last),但是與我們的本意,用n個val初始化原意相背,而其中有個*first,這里int去解引用必會報錯

改進(jìn):再多提供第一個參數(shù)是int重載版本的list(int n, const T& val = T())構(gòu)造函數(shù)

list(int n, const T& val = T())
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	for (size_t i = 0; i < n; ++i)
	{
		push_back(val);
	}
}

析構(gòu)函數(shù)

~list()
{
	clear();
	// 析構(gòu)與clear不同,要將哨兵位頭結(jié)點給刪除了
	delete _head;
	_head = nullptr;
}

list拷貝構(gòu)造函數(shù)

淺拷貝會崩潰的原因是,同一塊空間被析構(gòu)了兩次,所以我們要完成深拷貝

傳統(tǒng)寫法

// 傳統(tǒng)寫法
// lt2(lt1)
list(const list<T>& lt)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	for (auto e : lt)
	{
		push_back(e);
	}
}

注意: 因為要調(diào)用push_back函數(shù),push_back的前提是這個鏈表(lt2)已經(jīng)被初始化了,所以必須要先搞一個哨兵位頭結(jié)點,不然會崩潰

現(xiàn)代寫法

// 函數(shù)模板
template<class InputIterator>
list(InputIterator first, InputIterator last)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}
// lt2(lt1)
list(const list<T>& lt)
{
	_head = new Node();
	_head->_next = _head;
	_head->_prev = _head;
	list<T> tmp(lt.begin(), lt.end());
	std::swap(_head, tmp._head);
}

注意:lt2需要一個哨兵位頭結(jié)點

list賦值重載函數(shù)

傳統(tǒng)寫法

// lt2=lt1
list<T>& operator=(const list<T>& lt)
{
	if (this != lt)
	{
		clear();		 // 將lt2清空
		for (auto e : lt)// 再將值全部拷貝過去
		{
			push_back(e);
		}
	}
	return *this;
}

現(xiàn)代寫法

// 現(xiàn)代寫法
list<T>& operator=(list<T> lt)
{
	std::swap(_head, lt._head);
	return *this;
}

其他函數(shù)

// 清空
void clear()
{
	/*
	iterator it = begin();
	while (it!=end())
	{
		iterator del = it++;// 利用后置++,返回加加前的迭代器
		delete del._node;
	}
	// 最后剩下哨兵位頭結(jié)點
	_head->_next = _head;
	_head->_prev = _head;
	*/
	iterator it = begin();
	while (it != end())
	{
		erase(it++); // 也可以復(fù)用erase,it++返回加加之前的值
	}
}

到此這篇關(guān)于C++深入探究list的模擬實現(xiàn)的文章就介紹到這了,更多相關(guān)C++ list模擬實現(xiàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • C++中的命名空間詳細(xì)介紹

    C++中的命名空間詳細(xì)介紹

    大家好,本篇文章主要講的是C++中的命名空間詳細(xì)介紹,感興趣的同學(xué)趕快來看一看吧,對你有幫助的話記得收藏一下
    2022-02-02
  • C語言回溯法解八皇后問題(八皇后算法)

    C語言回溯法解八皇后問題(八皇后算法)

    這篇文章介紹了C語言回溯法解八皇后問題,文中通過示例代碼介紹的非常詳細(xì)。對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-12-12
  • OpenCV mask的作用及如何制作掩模mask

    OpenCV mask的作用及如何制作掩模mask

    mask 不是馬斯克,是掩膜,可以用來遮蓋非感興趣區(qū),突出感興趣區(qū),使得圖像處理只專注于ROI部分,OpenCV中的很多函數(shù)用到mask,mask是什么?怎么制作一個mask?本文給大家分享OpenCV mask的作用及如何制作掩模mask,感興趣的朋友一起看看吧
    2023-02-02
  • C語言 完整游戲項目推箱子詳細(xì)代碼

    C語言 完整游戲項目推箱子詳細(xì)代碼

    經(jīng)典的推箱子是一個的古老游戲,目的是在訓(xùn)練你的邏輯思考能力。在一個狹小的倉庫中,要求把木箱放到指定的位置,稍不小心就會出現(xiàn)箱子無法移動或者通道被堵住的情況,所以需要巧妙的利用有限的空間和通道,合理安排移動的次序和位置,才能順利的完成任務(wù)
    2021-11-11
  • C++實現(xiàn)LeetCode(73.矩陣賦零)

    C++實現(xiàn)LeetCode(73.矩陣賦零)

    這篇文章主要介紹了C++實現(xiàn)LeetCode(73.矩陣賦零),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • C語言實現(xiàn)貪吃蛇游戲設(shè)計

    C語言實現(xiàn)貪吃蛇游戲設(shè)計

    這篇文章主要為大家詳細(xì)介紹了C語言實現(xiàn)貪吃蛇游戲設(shè)計,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2020-07-07
  • C++ Boost Utility超詳細(xì)講解

    C++ Boost Utility超詳細(xì)講解

    Boost是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱。Boost庫是一個可移植、提供源代碼的C++庫,作為標(biāo)準(zhǔn)庫的后備,是C++標(biāo)準(zhǔn)化進(jìn)程的開發(fā)引擎之一,是為C++語言標(biāo)準(zhǔn)庫提供擴(kuò)展的一些C++程序庫的總稱
    2022-12-12
  • 內(nèi)核線程優(yōu)先級設(shè)置的方法介紹

    內(nèi)核線程優(yōu)先級設(shè)置的方法介紹

    本篇文章介紹了,內(nèi)核線程優(yōu)先級設(shè)置的方法。需要的朋友參考下
    2013-05-05
  • C++實現(xiàn)圖書館管理系統(tǒng)

    C++實現(xiàn)圖書館管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實現(xiàn)圖書館管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++數(shù)據(jù)結(jié)構(gòu)之實現(xiàn)鄰接表與鄰接矩陣的相互轉(zhuǎn)換

    C++數(shù)據(jù)結(jié)構(gòu)之實現(xiàn)鄰接表與鄰接矩陣的相互轉(zhuǎn)換

    這篇文章主要為大家學(xué)習(xí)介紹了C++如何實現(xiàn)鄰接表與鄰接矩陣的相互轉(zhuǎn)換,文中的示例代碼簡潔易懂,感興趣的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-07-07

最新評論

社旗县| 蒙阴县| 兴仁县| 承德县| 清涧县| 黄冈市| 大邑县| 江源县| 上杭县| 定日县| 论坛| 峡江县| 息烽县| 巴楚县| 龙海市| 邵阳县| 汕头市| 兴义市| 浏阳市| 湘乡市| 温州市| 静安区| 阿拉善盟| 苍山县| 林甸县| 启东市| 长岭县| 白水县| 清镇市| 花垣县| 卢湾区| 江孜县| 逊克县| 镇沅| 平罗县| 周至县| 赣榆县| 和田县| 塔城市| 永顺县| 循化|