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

C++list的模擬實(shí)現(xiàn)

 更新時(shí)間:2023年04月18日 11:08:32   作者:看到我請(qǐng)叫我滾去學(xué)習(xí)Orz  
list是數(shù)據(jù)結(jié)構(gòu)中的鏈表,在C++的STL中,有l(wèi)ist的模板,STL中的list的結(jié)構(gòu)是帶頭雙向循環(huán)鏈表,當(dāng)然STL中還有一個(gè)forward_list的鏈表,這個(gè)鏈表是一個(gè)帶頭的單鏈表。為了更好的理解list,我們來對(duì)其進(jìn)行模擬實(shí)現(xiàn)。,需要的朋友可以參考

一、節(jié)點(diǎn)的結(jié)構(gòu),list的迭代器的結(jié)構(gòu),以及l(fā)ist的結(jié)構(gòu)

1、節(jié)點(diǎn)的結(jié)構(gòu)

對(duì)于鏈表的節(jié)點(diǎn)我們都很熟悉了,節(jié)點(diǎn)中包含兩個(gè)域,一個(gè)指針域一個(gè)數(shù)據(jù)域,為了讓list能夠通用,我們選擇使用模板。
節(jié)點(diǎn)的結(jié)構(gòu)如下:

template<class T>
//struct也能定義類,默認(rèn)類的訪問限定符是 public
struct list_node
{
	//這個(gè)指針指向前一個(gè)節(jié)點(diǎn)
	list_node<T>* _prev;
	//這個(gè)指針指向后一個(gè)節(jié)點(diǎn)
	list_node<T>* _next;
	//這個(gè)是數(shù)據(jù)域中的元素
	T _data;
	
	//對(duì)節(jié)點(diǎn)使用匿名對(duì)象進(jìn)行初始化
	list_node(const T& data = T())
		:_prev(nullptr)
		,_next(nullptr)
		,_data(data)
	{}
};

2、迭代器的結(jié)構(gòu)

現(xiàn)在我們已經(jīng)有了節(jié)點(diǎn)了,我們還要有迭代器,如果沒有迭代器我們就不能很好的訪問每一個(gè)節(jié)點(diǎn)。
對(duì)于迭代器我們要讓它指向我們想要的節(jié)點(diǎn),這才能便于我們的訪問,于是很明顯我們迭代器的成員變量就要是一個(gè)節(jié)點(diǎn)的指針!同時(shí)為了讓list能夠通用,我們選擇使用模板來定義迭代器。
迭代器的結(jié)構(gòu)如下:

//這里后面的兩個(gè)參數(shù),在實(shí)際應(yīng)用時(shí)通常是T& , T* 或者是 const T& , const T*
//根據(jù)加與不加const 可以分別實(shí)例化出:普通正向迭代器與正向const迭代器
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	//將節(jié)點(diǎn)的類型進(jìn)行typedef方便使用
	typedef list_node<T> node;
	
	//將類自己進(jìn)行typedef方便使用
	typedef __list_iterator<T,Ref,Ptr> self;

	//成員變量 是一個(gè)指向節(jié)點(diǎn)的指針
	node* _pnode;

	//構(gòu)造函數(shù) 用一個(gè)節(jié)點(diǎn)的地址對(duì)迭代器進(jìn)行初始化,
	 __list_iterator(node* pnode)
		:_pnode(pnode)
	{}
};

3、list的結(jié)構(gòu)

由于list是帶頭雙向循環(huán)鏈表,我們只需要一個(gè)指向頭節(jié)點(diǎn)的指針便能夠管理所有的節(jié)點(diǎn)了。

template<class T>
class list
{
public:
	//將節(jié)點(diǎn)的類型進(jìn)行typedef方便使用
	typedef list_node<T> node;
	//將迭代器進(jìn)行typedef方便使用
	typedef __list_iterator<T, T&, T*> iterator;
	//將const迭代器進(jìn)行typedef方便使用
	typedef __list_iterator<T, const T&, const T*> const_iterator;
	//默認(rèn)構(gòu)造函數(shù)
	list()
	{
		empty_init();
	}
	//初始化函數(shù)
	void empty_init()
	{
		//申請(qǐng)一個(gè)頭節(jié)點(diǎn),將節(jié)點(diǎn)的地址給_head
		_head = new node;
		//讓哨兵位節(jié)點(diǎn)的 前指針指向自己
		_head->_prev = _head;
		//讓哨兵位節(jié)點(diǎn)的 后指針指向自己
		_head->_next = _head;
	}
private:
	//指向哨兵位節(jié)點(diǎn)的指針
	node* _head;
};

到此為止我們一共建立了三個(gè)類,下面我們模擬實(shí)現(xiàn)鏈表的各種接口時(shí),我們還要繼續(xù)豐富迭代器類的接口與list類的接口

二、迭代器的實(shí)現(xiàn)

由于鏈表的許多操作都要用到迭代器,但是迭代器的一些其他接口我們還沒有實(shí)現(xiàn),在這里我們來實(shí)現(xiàn)迭代器的所有接口。

1、*運(yùn)算符重載

對(duì)于原生指向節(jié)點(diǎn)的指針來說*運(yùn)算符能讓我們拿到節(jié)點(diǎn),但還無法拿到節(jié)點(diǎn)數(shù)據(jù)域中的數(shù)據(jù),但是對(duì)于迭代器來說*運(yùn)算符就要拿到容器中存儲(chǔ)的數(shù)據(jù),所以我們還要對(duì)迭代器的*運(yùn)算符進(jìn)行重載。

// *運(yùn)算符重載
Ref operator*()
{
	//迭代器中的那個(gè)指針不能是nullptr
	assert(_pnode);
	//返回節(jié)點(diǎn)中的數(shù)據(jù)域中的數(shù)據(jù)
	return _pnode->_data;
}

2、++ 與 --運(yùn)算符

++運(yùn)算符分為兩種:一種是前置++一種是后置++,這兩個(gè)函數(shù)構(gòu)成函數(shù)重載,后置++的參數(shù)部分會(huì)多一個(gè)int類型。(--運(yùn)算符同理)

對(duì)于原生指向節(jié)點(diǎn)的指針來說:++指針是讓指針移動(dòng)到下一個(gè)緊挨著的同類型的指針位置,但是對(duì)于迭代器來說:++是讓迭代器指向下一個(gè)節(jié)點(diǎn)的位置,這兩者并不匹配,所以我們要對(duì)++運(yùn)算符進(jìn)行函數(shù)重載。

//前置++運(yùn)算符
self& operator++()
{
	_pnode = _pnode->_next;
	return (*this);
}

//后置++運(yùn)算符
self operator++(int)
{
	//先保存++之前的結(jié)果
	self tmp(*this);
	_pnode = _pnode->_next;
	//返回++之前的值
	return tmp;
}

//前置--運(yùn)算符
self& operator--()
{
	_pnode = _pnode->_prev;
	return (*this);
}

//后置--運(yùn)算符
self operator--(int)
{
	self tmp(*this);
	_pnode = _pnode->_prev;
	return tmp;
}

3、->運(yùn)算符重載

雖然在前面我們已經(jīng)實(shí)現(xiàn)了迭代器*的運(yùn)算符重載,已經(jīng)可以訪問數(shù)據(jù)域中的數(shù)據(jù)了。但是當(dāng)我們的list里面存儲(chǔ)的是自定義類型的數(shù)據(jù),而我們想要訪問自定義類型中的成員變量時(shí)迭代器*的運(yùn)算符就不能夠幫到我們了。
例如:

struct Date
{
	int _year;
	int _month;
	int _day;
}
//it是迭代器,指向了存儲(chǔ)了Date類型的節(jié)點(diǎn)
//假設(shè):在沒有->操作符時(shí),我們想要修改_year的值,
(*it)._year = 2023;
//如果有了-> 操作符,我們就能這樣操作,更加符合我們的使用習(xí)慣
it->_year = 2023;

于是我們來實(shí)現(xiàn):->運(yùn)算符的重載,我們先來看代碼:

// ->運(yùn)算符重載
Ptr operator->()
{
	return &(_pnode->_data);
}

看到這里你可能會(huì)覺得很奇怪,覺得這段代碼是錯(cuò)誤的,下面我們就來詳細(xì)講解這里的問題和注意事項(xiàng)。

_pnode是迭代器的成員變量,是一個(gè)節(jié)點(diǎn)的指針,它使用的->是C++的內(nèi)置類型的操作符,這段代碼(_pnode->date) 是拿到的是節(jié)點(diǎn)中存儲(chǔ)的數(shù)據(jù),這段代碼&(_pnode->date) 是拿到的是節(jié)點(diǎn)中存儲(chǔ)的數(shù)據(jù)的地址,返回之后我們好像并沒有得到自定義類型中的數(shù)據(jù),好像還差一次->操作,比如這樣:

it->->_year = 2023; 
//it-> 等價(jià)于 (&(_pnode->date)) 

//(&(_pnode->date))->year = 2023;

實(shí)際上按上面的運(yùn)算符重載函數(shù)寫法確實(shí)是少了一次->,但是C++為了代碼的簡潔性在這里進(jìn)行了特殊處理,我們寫->的運(yùn)算符重載時(shí)只需要返回list里面自定義類型的地址就行了,在外面實(shí)際應(yīng)用時(shí),編譯器在編譯時(shí)會(huì)為我們自動(dòng)加上一次->。

4、 !=運(yùn)算符重載 與 ==運(yùn)算符重載

我們?cè)谑褂玫鬟M(jìn)行遍歷數(shù)據(jù)的時(shí)候,經(jīng)常要使用關(guān)系運(yùn)算符 != ==來判斷條件是否達(dá)到,在這里我們對(duì)關(guān)系運(yùn)算符 != ==進(jìn)行函數(shù)重載。
判斷兩個(gè)迭代器是否相等的辦法就是兩個(gè)迭代器是不是指向同一個(gè)位置!

// !=運(yùn)算符重載
bool operator!=(const self& s)
{
	return _pnode != s._pnode;
}

// ==運(yùn)算符重載
bool operator==(const self& s)
{
	return _pnode == s._pnode;
}

三、list的實(shí)現(xiàn)

在實(shí)現(xiàn)完迭代器之后,我們就要實(shí)現(xiàn)list的其他接口了。

1、迭代器接口

雖然在list的類外我們已經(jīng)實(shí)現(xiàn)了迭代器的各種接口,但是list類內(nèi)我們還沒有提供使用迭代器的接口的函數(shù),這個(gè)函數(shù)就是我們常用的begin()與end()函數(shù)!下面我們來一起實(shí)現(xiàn)一下。

//正向迭代器
iterator begin()
{
	//_head指向的是哨兵位的頭節(jié)點(diǎn),_head的下一個(gè)才是第一個(gè)節(jié)點(diǎn)!
	//這里使用的是一個(gè)指針構(gòu)造的匿名對(duì)象做返回值,編譯器會(huì)對(duì)此進(jìn)行優(yōu)化,能夠增加效率
	return iterator(_head->_next);
}

iterator end()
{
	//由于是雙向循環(huán)鏈表,所以最后一個(gè)節(jié)點(diǎn)的下一個(gè)位置就是哨兵位節(jié)點(diǎn)
	return iterator(_head);
}
//const迭代器的思路與普通迭代器類似
const_iterator begin() const
{
	return const_iterator(_head->_next);
}
const_iterator end() const 
{
	return const_iterator(_head);
}

2、插入函數(shù)

list鏈表的插入很簡單,我們需要先申請(qǐng)一個(gè)新節(jié)點(diǎn)存儲(chǔ)我們想要插入的數(shù)據(jù),然后將新節(jié)點(diǎn)的_prev指針指向前一個(gè)節(jié)點(diǎn),同時(shí)新節(jié)點(diǎn)的_next指針指向當(dāng)前節(jié)點(diǎn)。同時(shí)再對(duì)當(dāng)前節(jié)點(diǎn)與前一個(gè)節(jié)點(diǎn)中相應(yīng)的指針進(jìn)行更新,就完成了指針的鏈接。

void insert(iterator pos, const T& x)
{
	//先申請(qǐng)一個(gè)節(jié)點(diǎn),存儲(chǔ)我們要插入的數(shù)據(jù)
	node* new_node = new node(x);
	node* prev = pos._pnode->_prev;
	//鏈接過程
	prev->_next = new_node;
	new_node->_prev = prev;
	new_node->_next = pos._pnode;
	pos._pnode->_prev = new_node;
}

插入函數(shù)寫完以后,我們的頭插尾插函數(shù)也就相當(dāng)于寫完了

頭插函數(shù)

void push_front(const T& x)
{
	//在begin()位置進(jìn)行插入就是頭插!
	insert(begin(), x);
}

尾插函數(shù)

//尾插函數(shù)
void push_back(const T& x)
{
	//在end()位置進(jìn)行插入,就是尾插
	insert(end(), x);
}

3、刪除函數(shù)

鏈表的刪除沒有順序表那么復(fù)雜,但是我們應(yīng)該注意:應(yīng)該先將前后節(jié)點(diǎn)的連接關(guān)系給建立好,然后再刪除節(jié)點(diǎn)!

iterator erase(iterator pos)
{
	assert(pos != end());
	//鏈接過程
	node* prev = pos._pnode->_prev;
	node* next = pos._pnode->_next;
	prev->_next = next;
	next->_prev = prev;
	
	//刪除節(jié)點(diǎn)
	delete pos._pnode;
	//返回指向原節(jié)點(diǎn)的下一個(gè)節(jié)點(diǎn)的迭代器,外部接收后可以防止迭代器失效!
	return iterator(next);
}

同理刪除函數(shù)寫完以后,我們的頭刪尾刪函數(shù)也就相當(dāng)于寫完了!

頭刪函數(shù)

void pop_front()
{
	erase(begin());
}

尾刪函數(shù)

void pop_back()
{
	//由于end()是最后一個(gè)節(jié)點(diǎn)的下一個(gè)位置,所以這里要對(duì)end()進(jìn)行一次自減運(yùn)算
	erase(--end());
}

4、清除函數(shù)

清除函數(shù)的作用就是刪除除了哨兵位節(jié)點(diǎn)以外所有節(jié)點(diǎn),現(xiàn)在我們有了迭代器我們?cè)L問每個(gè)節(jié)點(diǎn)都變得非常容易,刪除相應(yīng)的節(jié)點(diǎn)也變的非常容易,我們只需要遍歷一遍鏈表逐一進(jìn)行刪除就行了。

void clear()
{
	list<T>::iterator it = begin();
	while (it != end())
	{
		//erase函數(shù)刪除相應(yīng)節(jié)點(diǎn)以后會(huì)返回下一個(gè)節(jié)點(diǎn)的迭代器
		it = erase(it);
	}
}

5、交換函數(shù)

對(duì)于鏈表的交換我們只需要交換list的成員變量中指向哨兵位節(jié)點(diǎn)的指針(即_head指針)就可以完成整個(gè)鏈表的交換了!

//swap函數(shù)
void swap(list<T>& lt)
{
	std::swap(_head, lt._head);
}

6、迭代器區(qū)間的構(gòu)造函數(shù)

此函數(shù)的作用就是用一個(gè)迭代器的區(qū)間來構(gòu)造一個(gè)鏈表,要實(shí)現(xiàn)這個(gè)函數(shù)我們只需要用迭代器進(jìn)行遍歷,然后將遍歷到的數(shù)據(jù)一個(gè)一個(gè)尾插就能構(gòu)成一個(gè)新的鏈表了,同時(shí)為了能夠支持更多的迭代器能夠去構(gòu)造鏈表,我們可以將該函數(shù)變成一個(gè)函數(shù)模板。

//迭代器區(qū)間構(gòu)造,傳入的迭代器應(yīng)該至少是一個(gè)二元迭代器,能支持向前和向后遍歷,這時(shí)鏈表的最低要求。
template<class Biditerator>
list(Biditerator first, Biditerator last)
{
	//調(diào)用初始化函數(shù)
	empty_init();
	//遍歷迭代器同時(shí)將數(shù)據(jù)形成一個(gè)新節(jié)點(diǎn)插入鏈表中
	while (first != last)
	{
		push_back(*first);
		++first;
	}
}

7、拷貝構(gòu)造

有了迭代器區(qū)間構(gòu)造和交換函數(shù)我們就可以寫現(xiàn)代寫法的拷貝構(gòu)造了!
現(xiàn)代寫法的拷貝構(gòu)造就是用迭代器區(qū)間構(gòu)造一個(gè)完整的鏈表,然后交換給拷貝對(duì)象。

//拷貝構(gòu)造
list(const list<T>& lt)
{
	//初始化
	empty_init();
	//用迭代器區(qū)間構(gòu)造創(chuàng)建一個(gè)新的list對(duì)象
	list<T> tmp(lt.begin(), lt.end());
	//將this指針指向的對(duì)象與這個(gè)新的tmp對(duì)象進(jìn)行交換,拷貝就變相完成了
	swap(tmp);
}

8、賦值重載

有了拷貝構(gòu)造和交換函數(shù),我們還是可以采用現(xiàn)代版本的賦值重載,原理與上面的拷貝構(gòu)造同理。

//賦值運(yùn)算符重載
//注意這里的傳參方式是傳值傳參
list<T>& operator=(list<T> lt)
{
	//將this指針指向的對(duì)象與這個(gè)lt對(duì)象進(jìn)行交換,賦值就變相完成了
	swap(lt);
	return (*this);
}

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

最后就是析構(gòu)函數(shù)了,由于我們已經(jīng)實(shí)現(xiàn)過了clear函數(shù),所以我們可以先調(diào)用clear函數(shù)刪除所有有效節(jié)點(diǎn),然后再刪除哨兵位的節(jié)點(diǎn)就行了!

~list()
{
	clear();
	delete _head;
	_head = nullptr;
}

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

相關(guān)文章

  • 關(guān)于C++的重載運(yùn)算符和重載函數(shù)

    關(guān)于C++的重載運(yùn)算符和重載函數(shù)

    一般來說,重載運(yùn)算符在實(shí)際的項(xiàng)目開發(fā)中會(huì)經(jīng)常的用到,但如果某些自定義類型通過簡短幾行代碼重載一些常用的運(yùn)算符(如:+-*/),就能讓編程工作帶來方便,需要的朋友可以參考下本文
    2023-05-05
  • C語言中的文件讀寫fseek 函數(shù)

    C語言中的文件讀寫fseek 函數(shù)

    這篇文章主要介紹是我是C語言中的文件讀寫fseek 函數(shù)的相關(guān)資料,fseek 函數(shù)用來移動(dòng)文件流的讀寫位置;就好比播放器,可以直接拖拽到精彩的時(shí)間點(diǎn)一樣,下面我們就來詳細(xì)介紹該內(nèi)容吧,感興趣的小伙伴可以參考一下
    2021-10-10
  • EasyC++?右值引用

    EasyC++?右值引用

    這篇文章主要介紹了C++?右值引用,右值引用指的是以引用傳遞(而非值傳遞)的方式使用?C++?右值,下面文章將對(duì)此詳細(xì)介紹,需要的朋友可以參考一下,希望對(duì)你有所幫助
    2021-12-12
  • C++ 實(shí)現(xiàn)優(yōu)先隊(duì)列的簡單實(shí)例

    C++ 實(shí)現(xiàn)優(yōu)先隊(duì)列的簡單實(shí)例

    這篇文章主要介紹了C++ 實(shí)現(xiàn)優(yōu)先隊(duì)列的簡單實(shí)例的相關(guān)資料,希望通過本文能幫助大家實(shí)現(xiàn)優(yōu)先隊(duì)列,需要的朋友可以參考下
    2017-08-08
  • OpenCV圖像特征提取之Shi-Tomasi角點(diǎn)檢測算法詳解

    OpenCV圖像特征提取之Shi-Tomasi角點(diǎn)檢測算法詳解

    Harris角點(diǎn)檢測算法就是對(duì)角點(diǎn)響應(yīng)函數(shù)R進(jìn)行閾值處理,Shi-Tomasi原理幾乎和Harris一樣的,只不過最后計(jì)算角點(diǎn)響應(yīng)的公式發(fā)生了變化。本文將和大家詳細(xì)說說Shi-Tomasi角點(diǎn)檢測算法的原理與實(shí)現(xiàn),需要的可以參考一下
    2022-09-09
  • C++結(jié)構(gòu)體數(shù)組詳細(xì)解析

    C++結(jié)構(gòu)體數(shù)組詳細(xì)解析

    定義結(jié)構(gòu)體數(shù)組和定義結(jié)構(gòu)體變量類似,定義結(jié)構(gòu)體數(shù)組時(shí)只需聲明其為數(shù)組即可
    2013-10-10
  • C 語言快速排序?qū)嵗a

    C 語言快速排序?qū)嵗a

    本文主要介紹了C語言的快速排序算法,這里給大家舉例說明并附代碼實(shí)例,需要的朋友可以參考下
    2016-07-07
  • 純c實(shí)現(xiàn)異常捕獲try-catch組件教程示例

    純c實(shí)現(xiàn)異常捕獲try-catch組件教程示例

    這篇文章主要為大家介紹了純c實(shí)現(xiàn)異常捕獲try-catch組件教程示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2022-08-08
  • C++數(shù)據(jù)結(jié)構(gòu)之AVL樹的實(shí)現(xiàn)

    C++數(shù)據(jù)結(jié)構(gòu)之AVL樹的實(shí)現(xiàn)

    AVL樹是高度平衡的而二叉樹,它的特點(diǎn)是AVL樹中任何節(jié)點(diǎn)的兩個(gè)子樹的高度最大差別為1,本文主要給大家介紹了C++如何實(shí)現(xiàn)AVL樹,需要的朋友可以參考下
    2022-06-06
  • C語言課程設(shè)計(jì)之停車場管理問題

    C語言課程設(shè)計(jì)之停車場管理問題

    這篇文章主要為大家詳細(xì)介紹了C語言課程設(shè)計(jì)之停車場管理問題,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-03-03

最新評(píng)論