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

詳解C++ STL模擬實(shí)現(xiàn)forward_list

 更新時間:2023年01月13日 14:07:15   作者:叫我小秦就好了  
forward_list是C++ 11新增的容器,它支持從容器中的任何位置快速插入和移除元素的容器,不支持快速隨機(jī)訪問。本文將模擬實(shí)現(xiàn)forward_list,感興趣的可以了解一下

forward_list 概述

forward_list 是 C++ 11 新增的容器,它的實(shí)現(xiàn)為單鏈表。

forward_list 是支持從容器中的任何位置快速插入和移除元素的容器,不支持快速隨機(jī)訪問。forward_list 和 list 的主要區(qū)別在于,前者的迭代器屬于單向的 Forward Iterator,后者的迭代器屬于雙向的 Bidirectional Iterator。

下面實(shí)現(xiàn)的單鏈表為單向帶頭循環(huán)鏈表,實(shí)現(xiàn)為循環(huán)鏈表只是因為這樣實(shí)現(xiàn)比較簡單,更容易處理 end 的指向。

文章完整代碼:ForwardList · 秦1230/STL study

接口總覽

namespace qgw {
	/// @brief forward_list 中每個節(jié)點(diǎn)
	/// @tparam T 節(jié)點(diǎn)存儲的數(shù)據(jù)類型
	template <class T>
	struct _forward_list_node {
		_forward_list_node(const T& data = T());	// 節(jié)點(diǎn)類的構(gòu)造函數(shù)
			
		_forward_list_node<T>* _next;				// 指向后一節(jié)點(diǎn)
		T _data;									// 存儲節(jié)點(diǎn)數(shù)據(jù)
	};

	/// @brief forward_list 的迭代器
	/// @tparam T forward_list 數(shù)據(jù)的類型
	/// @tparam Ref 數(shù)據(jù)的引用類型
	/// @tparam Ptr 數(shù)據(jù)的指針類型
	template <class T, class Ref, class Ptr>
	struct _forward_list_iterator {
		typedef _forward_list_iterator<T, T&, T*>		iterator;
		typedef _forward_list_iterator<T, Ref, Ptr>		self;

		typedef Ptr pointer;
		typedef Ref reference;
		typedef _forward_list_node<T> forward_list_node;

		// 構(gòu)造函數(shù)
		_forward_list_iterator(forward_list_node* node = nullptr);

		// 各種運(yùn)算符重載
		bool operator==(const self& x) const;
		bool operator!=(const self& x) const;
		reference operator*() const;
		pointer operator->() const;
		self& operator++();
		self operator++(int);

		forward_list_node* _node;	// 指向?qū)?yīng)的 forward_list 節(jié)點(diǎn)
	};

	template <class T>
	class forward_list {
	public:
		typedef T* pointer;
		typedef const T* const_pointer;
		typedef T& reference;
		typedef const T& const_reference;
		typedef _forward_list_node<T>	forward_list_node;
		typedef _forward_list_iterator<T, T&, T*>             iterator;
		typedef _forward_list_iterator<T, const T&, const T*> const_iterator;

	public:
		// 默認(rèn)成員函數(shù)
		forward_list();
		forward_list(const forward_list<T>& other);
		forward_list<T>& operator=(const forward_list<T>& other);
		~forward_list();

		// 元素訪問
		reference front();
		const_reference front() const;

		// 迭代器
		iterator begin();
		const_iterator begin() const;
		iterator end();
		const_iterator end() const;

		// 容量
		bool empty() const;

		// 修改器
		void clear();
		iterator insert_after(iterator pos, const_reference val);
		void push_front(const_reference val);
		iterator erase_after(iterator pos);
		void pop_front();
		void swap(forward_list& other);

	private:
		forward_list_node* _node;
	};
}

forward_list 的節(jié)點(diǎn)

forward_list 節(jié)點(diǎn)的設(shè)計與 list 的節(jié)點(diǎn)類似,只需兩個成員變量:一個節(jié)點(diǎn)指針和數(shù)據(jù)。

構(gòu)造函數(shù)將數(shù)據(jù)初始化為給定數(shù)據(jù),再將 _next 指針初始化為空。

/// @brief 節(jié)點(diǎn)類的構(gòu)造函數(shù)
/// @param data 用來構(gòu)造節(jié)點(diǎn)的初值
_forward_list_node(const T& data = T())
    : _data(data) {
        _next = nullptr;
    }

默認(rèn)成員函數(shù)

默認(rèn)構(gòu)造函數(shù)

因為實(shí)現(xiàn)的是的單向帶頭循環(huán)鏈表,所以要在構(gòu)造函數(shù)創(chuàng)建一個頭節(jié)點(diǎn),并將 _next 指針指向自己。

/// @brief 構(gòu)造一個空鏈表
forward_list() {
    _node = new forward_list_node;    // 創(chuàng)建一個頭節(jié)點(diǎn)
    _node->_next = _node;            // 后面指向自己
}

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

forward_list 的析構(gòu)函數(shù),先調(diào)用 clear 釋放數(shù)據(jù)資源,再 delete 掉頭節(jié)點(diǎn)即可。

/// @brief 釋放資源
~forward_list () {
    clear();
    delete _node;
    _node = nullptr;
}

forward_list 的迭代器

forward_list 的節(jié)點(diǎn)在內(nèi)存中不是連續(xù)存儲的,因此不能使用原生指針作為 forward_list 的迭代器。

由于 forward_list 是一個單向鏈表,迭代器必須具備后移的能力,所以 forward_list 提供的是 Forward Iterator。

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

forward_list 的迭代器中成員變量只有一個節(jié)點(diǎn)指針,將其初始化為給定的節(jié)點(diǎn)指針。

/// @brief 迭代器的構(gòu)造函數(shù)
/// @param node 用來構(gòu)造的節(jié)點(diǎn)
_forward_list_iterator(forward_list_node* node = nullptr) {
    _node = node;
}

operator==

兩個 forward_list 迭代器的比較,實(shí)際上比較的是迭代器所指向的節(jié)點(diǎn),指向同一節(jié)點(diǎn)即為兩迭代器相同。

/// @brief 判斷兩迭代器指向的節(jié)點(diǎn)是否相同
/// @param x 用來比較的迭代器
/// @return 相同返回 true,不同返回 false
bool operator==(const self& x) const {
    return _node == x._node;
}

operator!=

operator!= 的實(shí)現(xiàn)可以借助 operator==,直接調(diào)用判斷是否相等的函數(shù),然后返回相反的結(jié)果。

/// @brief 判斷兩迭代器指向的節(jié)點(diǎn)是否不同
/// @param x 用來比較的迭代器
/// @return 不同返回 true,相同返回 false
bool operator!=(const self& x) const {
    return !operator==(x);
}

operator*

當(dāng)我們對一個指針進(jìn)行解引用時會發(fā)生什么,我們會得到指針指向的數(shù)據(jù)。同理,我們對迭代器進(jìn)行解引用,得到的是迭代器中節(jié)點(diǎn)指針?biāo)赶蜃兞康闹怠?/p>

/// @brief 獲取指向節(jié)點(diǎn)中的數(shù)據(jù)值
/// @return 返回指向節(jié)點(diǎn)數(shù)據(jù)的引用
reference operator*() const {
    return (*_node)._data;
}

operator->

假如我們有如下數(shù)據(jù)類:

// 有一個 Person 類,里面有身高和體重兩個成員
struct Person {
    double height;
    double weight;
};

此時,我們的數(shù)據(jù)不再是單一的變量了,而是一個結(jié)構(gòu)體變量。我們想讀取其中的數(shù)據(jù),該怎么操作呢?

Person p1 = { 165, 105 };
Person* p = &p1;
cout << (*p).height << endl;    // 獲取身高數(shù)據(jù)
cout << p->weight << endl;        // 獲取體重數(shù)據(jù)

我們可以先對直接解引用得到一個 Person 對象,再用 . 操作符訪問其中的變量。

當(dāng)然我們也可以選擇對 Person* 使用 -> 操作符訪問結(jié)構(gòu)體內(nèi)的變量。

于是乎,operator-> 的功能也就很清晰了。當(dāng)我們對迭代器使用 -> 時我們可以直接訪問節(jié)點(diǎn)中的變量。也就是說,我們有一迭代器 iter,其中迭代器中存儲的數(shù)據(jù)類型為 Person,當(dāng)我們使用 iter->height 時,可以直接獲取到身高。

/// @brief 獲取節(jié)點(diǎn)中數(shù)據(jù)的地址
/// @return 返回節(jié)點(diǎn)指向的數(shù)據(jù)的地址
pointer operator->() const {
    return &(operator*());
}

看了實(shí)現(xiàn)你可能會疑惑 iter-> 獲得的明明是結(jié)構(gòu)體的指針,后面應(yīng)該再跟一個 -> 箭頭才對。是的沒錯,確實(shí)應(yīng)該是這樣,不過 iter->->height 的可讀性比較差,所以編譯器會在編譯時自動為我們添加一個箭頭。

operator++

operator++ 運(yùn)算符的作用是讓迭代器指向 forward_list 中下一節(jié)點(diǎn)。因為 forward_list 是單鏈表不能向前移動,所以不用實(shí)現(xiàn) operator--。

前置實(shí)現(xiàn)的思路是:通過迭代器中的節(jié)點(diǎn)指針找到下一節(jié)點(diǎn),然后賦值給迭代器中的節(jié)點(diǎn)指針。

后置實(shí)現(xiàn)的思路是:先拷貝一份當(dāng)前位置的迭代器,然后調(diào)用前置 ++,最后返回臨時變量。

需要注意的是:前置 ++ 返回的是前進(jìn)后迭代器的引用,后置 ++ 返回的是一個臨時變量。

/// @brief 前置 ++
/// @return 返回前進(jìn)一步后的迭代器
self& operator++() {
    _node = _node->_next;
    return *this;
}

/// @brief 后置 ++
/// @param  無作用,只是為了與前置 ++ 進(jìn)行區(qū)分,形成重載
/// @return 返回當(dāng)前的迭代器
self operator++(int) {
    self tmp = *this;
    ++(*this);
    return tmp;
}

元素訪問

front

front 獲取容器首元素的引用,調(diào)用 begin 得到首元素的迭代器,再解引用即可。

因為 forward_list 的迭代器只能單向移動,故不能快速獲得鏈表中最后一個節(jié)點(diǎn)。

/// @brief 返回容器首元素的引用
/// @return 首元素的引用
reference front() {
    return *begin();
}

// 與上面唯一不同就是用于 const 容器
const_reference front() const {
    return *begin();
}

迭代器

begin

begin 獲取的是首元素的迭代器,根據(jù)上圖,直接返回頭節(jié)點(diǎn)的下一位置即可。

/// @brief 返回指向 forward_list 首元素的迭代器
/// @return 指向首元素的迭代器
iterator begin() {
    // 根據(jù)節(jié)點(diǎn)指針構(gòu)造迭代器
    return iterator(_node->_next);
}

// const 版本供 const 容器使用
const_iterator begin() const {
    return const_iterator(_node->_next);
}

end

end 獲取的是最后一個元素下一個位置的迭代器,根據(jù)上圖就是 _node 所指向的節(jié)點(diǎn)。

/// @brief 返回指向 forward_list 末元素后一元素的迭代器
/// @return 指向最后元素下一位置的迭代器
iterator end() {
    // 調(diào)用 iterator 構(gòu)造函數(shù)
    return iterator(_node);
}

const_iterator end() const {
    return const_iterator(_node);
}

容量

empty

begin 和 end 指向相同,說明鏈表此時只有一個頭節(jié)點(diǎn),鏈表為空。

/// @brief 檢查容器是否無元素
/// @return 若容器為空則為 true,否則為 false
bool empty() const {
    return begin() == end();
}

修改器

insert_after

根據(jù) STL 的習(xí)慣,插入操作會將新元素插入于指定位置之前,而非之后。然而 forward_list 是一個單向鏈表,它沒有任何方便的方法可以定位出前一個位置,它必須從頭找。因此,forward_list 中提供的插入操作,是插入在指定位置之后。

下圖為:只有 0、1 兩個元素的單鏈表,在 0 之后插入元素值為 2 的節(jié)點(diǎn)的示意圖。

插入的過程非常簡單:

1.創(chuàng)建一個要插入的節(jié)點(diǎn)

2.插入節(jié)點(diǎn)的 _next 指向 pos 后一位置的節(jié)點(diǎn)

3.pos 的 _next 指向要插入的節(jié)點(diǎn)

/// @brief 在容器中的指定位置后插入元素
/// @param pos 內(nèi)容將插入到其后的迭代器
/// @param val 要插入的元素值
/// @return 指向被插入元素的迭代器
iterator insert_after(iterator pos, const_reference val) {
    forward_list_node* tmp = new forward_list_node(val);    // 創(chuàng)建要插入的節(jié)點(diǎn)
    tmp->_next = pos._node->_next;                            // (1)
    pos._node->_next = tmp;                                    // (2)
    return tmp;
}

push_front

push_front 的作用是在容器起始處插入元素。

直接調(diào)用 insert_after 插入就行,需要注意的是,insert_after 是在給定位置之后插入,所以應(yīng)傳入頭節(jié)點(diǎn)對應(yīng)的迭代器位置。

/// @brief 頭插給定元素 val 到容器起始
/// @param val 要頭插的元素值
void push_front(const_reference val) {
    // end() 返回頭節(jié)點(diǎn)位置的迭代器,在這之后插入是頭插
    insert_after(end(), val);
}

erase_after

下圖為:有三個元素 0、1、2 的鏈表,刪除 pos 指向節(jié)點(diǎn)之后節(jié)點(diǎn)(值為 1)的示意圖。

刪除的過程非常簡單:

1.記錄 pos 的下一節(jié)點(diǎn) nextNode

2.將 pos 的 _next 指向 nextNode 的下一個節(jié)點(diǎn)

3.delete 釋放掉 nextNode 所指向的節(jié)點(diǎn)

/// @brief 從容器移除 pos 后面一個元素
/// @param pos 指向要被移除元素前一個元素的迭代器
/// @return 最后移除元素之后的迭代器
iterator erase_after(iterator pos) {
    forward_list_node* nextNode = pos._node->_next;        // 記錄 pos 指向節(jié)點(diǎn)的下一節(jié)點(diǎn)
    pos._node->_next = nextNode->_next;                    // (1)
    delete (nextNode);
    return (iterator)(pos._node->_next);
}

pop_front

pop_front 移除容器的首元素,也就是 end 指向的下一節(jié)點(diǎn)。

/// @brief 移除容器首元素
void pop_front() {
    erase_after(end());
}

clear

clear 用于清空容器所有數(shù)據(jù),不清理頭節(jié)點(diǎn)。

要注意 erase_after 刪除給定位置下一個節(jié)點(diǎn),end 的下一個是第一個元素,這樣以次刪除直到容器為空,即只剩一個頭節(jié)點(diǎn)。

/// @brief 從容器擦除所有元素
void clear() {
    while (!empty()) {
        erase_after(end());
    }
}

swap

swap 用來交換兩個 forward_list容器,不用交換 forward_list 中每個元素的值,直接交換 _node 指針即可。

/// @brief 將內(nèi)容與 other 的交換
/// @param other 要與之交換內(nèi)容的容器
void swap(forward_list& other) {
    std::swap(_node, other._node);
}

以上就是詳解C++ STL模擬實(shí)現(xiàn)forward_list的詳細(xì)內(nèi)容,更多關(guān)于C++ STL forward_list的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • C語言數(shù)據(jù)結(jié)構(gòu)不掛科指南之線性表詳解

    C語言數(shù)據(jù)結(jié)構(gòu)不掛科指南之線性表詳解

    線性表是由?n(n≥0)個數(shù)據(jù)元素組成的有窮序列,這篇文章主要來和大家來了C語言數(shù)據(jù)結(jié)構(gòu)中的線性表,感興趣的小伙伴可以跟隨小編一起了解一下
    2022-09-09
  • Matlab繪制有趣的羅盤時鐘的示例代碼

    Matlab繪制有趣的羅盤時鐘的示例代碼

    這篇文章主要介紹了如何利用Matlab實(shí)現(xiàn)繪制有趣的羅盤時鐘,文中的示例代碼講解詳細(xì),對我們學(xué)習(xí)Matlab有一定的幫助,需要的可以參考一下
    2023-01-01
  • C++實(shí)現(xiàn)LeetCode(64.最小路徑和)

    C++實(shí)現(xiàn)LeetCode(64.最小路徑和)

    這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(64.最小路徑和),本篇文章通過簡要的案例,講解了該項技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下
    2021-07-07
  • Qt QFrame的具體使用

    Qt QFrame的具體使用

    本文主要介紹了Qt QFrame的具體使用,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-06-06
  • C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng)

    C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng)

    這篇文章主要為大家詳細(xì)介紹了C++實(shí)現(xiàn)景區(qū)旅游信息管理系統(tǒng),文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-03-03
  • C++迭代器介紹(iterator、const_iterator、reverse_interator、const_reverse_interator)

    C++迭代器介紹(iterator、const_iterator、reverse_interator、const_rev

    這篇文章主要介紹了C++迭代器介紹(iterator、const_iterator、reverse_interator、const_reverse_interator),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-02-02
  • 基于OpenCV讀取攝像頭實(shí)現(xiàn)單個人臉驗證MFC程序

    基于OpenCV讀取攝像頭實(shí)現(xiàn)單個人臉驗證MFC程序

    這篇文章主要為大家詳細(xì)介紹了基于OpenCV讀取攝像頭實(shí)現(xiàn)單個人臉驗證MFC程序,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-08-08
  • C語言實(shí)現(xiàn)水波紋效果

    C語言實(shí)現(xiàn)水波紋效果

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)水波紋效果,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-02-02
  • C語言實(shí)現(xiàn)萬年歷效果

    C語言實(shí)現(xiàn)萬年歷效果

    這篇文章主要為大家詳細(xì)介紹了C語言實(shí)現(xiàn)萬年歷效果,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2019-11-11
  • Mygui中文換行問題解決方案

    Mygui中文換行問題解決方案

    相信大家解決了中文輸入后一定會遇到如何解決中文輸入的問題,中文輸入換行問題是很多gui框架都存在的一個問題,需要的朋友可以了解下
    2012-11-11

最新評論