list簡單模擬實現

  • 成員變量
  • 迭代器(重點)
    • ListIterator
    • 運算符重載
    • begin、end
  • 插入、刪除
    • insert
    • erase
    • 頭插、尾插、頭刪、尾刪
  • operator->
  • const_iterator
  • 拷貝構造
  • operator=
  • 析構函數
  • 完整代碼

由于前面已經模擬實現了vector,所以這里關于一些函數實現就不會講的過于細致。

成員變量

template<class T>
struct ListNode
{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}
};template<class T>
class list
{typedef ListNode<T> Node;
public:list(){_head = new Node;_head->_next = _head->_prev = _head;_size = 0;}size_t size()const{return _size;}
private:Node* _head;size_t _size;
};

迭代器(重點)

我們想要迭代器能實現以下的功能:

void test_1()
{list<int>lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';++it;}cout << endl;
}

實際上迭代器模擬的是指針的功能,然而鏈表的指針++并不會得到下一個元素的指針。但我們卻希望迭代器++能得到下一個元素的迭代器。

那么我們是不是需要對++進行一個修改,也就是運算符重載。

再仔細想想,重載后的運算符是對這一個類生效的,但我們只需要對迭代器生效。那是不是意味著我們的迭代器需要是一個獨立的類:

ListIterator

template<class T>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;//內置類型指針ListIterator(Node* node):_node(node){}
}

注意不需要析構函數,實際上我們的迭代器就是原數組結點指針的一個類,怎么可以用過一次后就析構掉呢?

運算符重載

前置++:

Self& operator++()//前置++
{_node = _node->_next;return *this;
}

后置++:

Self operator++(int)//后置++
{Self tmp(*this);//淺拷貝_node = _node->_next;return tmp; 
}

值得注意的是,前后++的重載函數名都是operator++,那么如何區分他們呢?

C++語法規定:后置++的函數參數里面需要有一個int

此外,我這的tmp是一個淺拷貝,這樣做有沒有問題呢?
我們需要的是指針,那么對于指針自然應該采用淺拷貝。
事實上,我們一般有一個規律:如果類需要重載析構函數,那么一般就要深拷貝,反之一般就需要淺拷貝。

剩余運算符:

Self& operator*()
{return _node->_data;
}Self& operator--()//前置--
{_node = _node->_prev;return *this;
}Self operator--(int)//后置--
{Self tmp(*this);//淺拷貝_node = _node->_prev;return tmp;
}bool operator!=(const Self& it)
{return _node != it._node;
}bool operator==(const Self& it)
{return _node == it._node;
}

begin、end

注意到我們的begin是返回首元素迭代器,而end是返回最后一個元素的下一個位置的迭代器:

typedef ListIterator<T> iterator;
iterator begin() 
{return _head->_next;
}iterator end()
{return iterator(_head);
}

注意到,begin這里我返回的是_head->_next,因為單參數會隱式類型轉換:自動調用單參數的構造函數,也就是等價于iterator(_head->_next).

插入、刪除

insert

void insert(iterator pos, const T& val)
{Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;
}

erase

iterator erase(iterator pos)//返回iterator防止迭代器失效
{Node* cur = pos->_node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);
}

值得注意的是,為了防止迭代器失效,我們這里要返回最后一個刪除的元素的下一個元素的迭代。

頭插、尾插、頭刪、尾刪

void push_back(const T& x)
{insert(end(), x);
}void push_front(const T& x)
{insert(begin(), x);
}void pop_back()
{erase(end()--);//沒有重載-1,所以要用--
}void pop_front()
{erase(begin());
}

尾刪這里需要注意一定要是end()–,而不能是end()-1.理由很簡單,因為我們沒有重載-。

operator->

對于非內置類型如下:

struct A
{int _a1;int _a2;A(int a1 = 0, int a2 = 0):_a1(a1),_a2(a2){}
};void test_2()
{list<A>lt;A aa1(1, 1);lt.push_back(aa1);lt.push_back(aa1);lt.push_back(A(2, 2));lt.push_back({ 3,3 });lt.push_back({ 4,4 });list<A>::iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';//不支持流插入++it;}}

不難發現cout << *it << ’ ';這里會報錯,因為自定義類型沒有重載<<。
因此我們需要改寫為:

cout << (*it)._a1 << ' ' << (*it)._a2 << endl;

不難發現這有些許繁瑣,正常來說對于類指針功能我們更希望用->這個操作符,也就是:

cout << it->_a1 << ' ' << it->_a2 << endl;

那就需要重載->:

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

這時候就有人發出疑問了,怎么返回值是一個指針,那要調用_a1,不應該是it->->_a1嗎?
沒錯,這是原生寫法,但是我們C++語法給他優化掉了,現在it->_a1等價于it.operator->()->_a1

const_iterator

const_iterator為什么不是const iterator?
這個問題其實前面學習類的時候已經解答過了,事實上我們的const_iterator是不能改變迭代器呢,還是不能改變迭代器指向的元素的值呢?
事實上const_iterator也是可以++的,否則怎么遍歷數組元素。
那也就是說const_iterator只是不能改變指向的元素的值,而不是不能改變迭代器本身。

經過上述分析我們得知,我們只需要對*和->重載的返回值作出修改即可:

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

但是,只有返回值不同是無法重載函數的!!
那么我們是不是需要再多寫一個const_iterator類呢?
可以,但有更好的方法。
注意到我們只有這兩個函數不同,并且只有返回值不同,那是不是可以寫一個模板將返回值不同分成不同的類呢?

template<class T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}}
typedef ListIterator<T, T&, T*> iterator;
typedef ListIterator<T, const T&, const T*> const_iterator;const_iterator begin() const
{return _head->_next;
}const_iterator end() const
{return _head;
}

這樣我們就省去了CV再寫一個類的功夫。

拷貝構造

一樣是復用push_back

void empty_init()
{_head = new Node;_head->_next = _head->_prev = _head;_size = 0;
}
list(const T& lt)// 復用push_back來深拷貝
{empty_init();for (auto& e : lt){push_back(e);}
}

operator=

一樣是復用拷貝構造:

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}list<T>& operator=(list<T> lt)//傳值傳參
{swap(lt);return *this;
}

析構函數

同樣復用erase

void clear()//沒清除頭結點
{iterator it = begin();while (it != end()){it = erase(it);}}~list()
{clear();delete _head;_head = nullptr;}

完整代碼

template<class T>
struct ListNode
{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}
};template<class T,class Ref,class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;//內置類型指針ListIterator(Node* node):_node(node){}Ref operator*(){return _node->_data;}Ptr operator->(){return &_node->_data;}Self& operator++()//前置++{_node = _node->_next;return *this;}Self operator++(int)//后置++{Self tmp(*this);//淺拷貝_node = _node->_next;return tmp; }Self& operator--()//前置--{_node = _node->_prev;return *this;}Self operator--(int)//后置--{Self tmp(*this);//淺拷貝_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};template<class T>
class list
{typedef ListNode<T> Node;
public:typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;iterator begin() //const{return iterator(_head->_next);}iterator end(){return iterator(_head);}//const_iterator而不是const iterator,因為是指向內容不能被修改,而非迭代器本身不能被修改。如果是const iterator就不能it++const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}void empty_init(){_head = new Node;_head->_next = _head->_prev = _head;_size = 0;}list(){empty_init();}list(const T& lt)// 復用push_back來深拷貝{empty_init();for (auto& e : lt){push_back(e);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt)//傳值傳參{swap(lt);return *this;}void clear()//沒清除頭結點{iterator it = begin();while (it != end()){it = erase(it);}}~list(){clear();delete _head;_head = nullptr;}//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;//}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(end()--);//沒有重載-1,所以要用--}void pop_front(){erase(begin());}void insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;}iterator erase(iterator pos)//返回iterator防止迭代器失效{Node* cur = pos->_node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);}size_t size()const{return _size;}bool empty()const{return _size == 0;}private:Node* _head;size_t _size;
};

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/79970.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/79970.shtml
英文地址,請注明出處:http://en.pswp.cn/web/79970.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

【計算機視覺】基于Python的相機標定項目Camera-Calibration深度解析

基于Python的相機標定項目Camera-Calibration深度解析 1. 項目概述技術核心 2. 技術原理與數學模型2.1 相機模型2.2 畸變模型 3. 實戰指南&#xff1a;項目運行與標定流程3.1 環境配置3.2 數據準備3.3 執行步驟3.4 結果驗證 4. 常見問題與解決方案4.1 角點檢測失敗4.2 標定結果…

多光譜影像:解鎖遙感奧秘的 “彩色鑰匙”

在遙感領域&#xff0c;多光譜影像猶如一把神奇的 “彩色鑰匙”&#xff0c;為我們開啟洞察地球表面與大氣層的全新視角。 圖片來源于星圖云開放平臺 多光譜影像&#xff0c;顧名思義&#xff0c;就是利用遙感平臺上的多光譜傳感器&#xff0c;同時對地球目標地物在多個不同光譜…

【ROS2】ROS節點啟動崩潰:rclcpp::exceptions::RCLInvalidArgument

1、問題描述 啟動ROS節點時,直接崩潰,打印信息如下: terminate called after throwing an instance of rclcpp::exceptions::RCLInvalidArgumentwhat(): failed to create guard condition: context argument is null, at ./src/rcl/guard_condition.c:65 [ros2run]: Abo…

MinerU安裝(pdf轉markdown、json)

在Windows上安裝MinerU&#xff0c;參考以下幾個文章&#xff0c;可以成功安裝&#xff0c;并使用GPU解析。 整體安裝教程&#xff1a; MinerU本地化部署教程——一款AI知識庫建站的必備工具 其中安裝conda的教程&#xff1a; 一步步教你在 Windows 上輕松安裝 Anaconda以及使…

aws 實踐創建policy + Role

今天Cyber 通過image 來創建EC2 的時候,要添加policy, 雖然是administrator 的role, 參考Cyber 提供的link: Imageshttps://docs.cyberark.com/pam-self-hosted/14.2/en/content/pas%20cloud/images.htm#Bring 1 Step1:

【ROS2】編譯Qt實現的庫,然后鏈接該庫時,報錯:/usr/bin/ld: XXX undefined reference to `vtable for

1、問題描述 在ROS2工程中,編譯使用Qt實現的庫,在其它ROS2包鏈接該庫時,報錯: /usr/bin/ld: XXX undefined reference to `vtable for2、原因分析 查看鏈接失敗的幾個函數接口都是,信號函數(signals 標記的函數)。因為信號函數都只有定義,沒有實現,在執行ROS2 colc…

數據庫--處理模型(Processing Model)(二)

執行查詢的方法有很多,接下來將介紹以更高效和更有效率的方式執行分析工作負載(在OLAP系統中)的不同技術,包括以下內容: 執行并行性(Execution Parallelism)執行引擎(Execution Engines)執行操作符輸出(Execution Operator Output)中間數據表示(Intermediate Data …

PostgreSQL pgrowlocks 擴展詳解

一、簡介 pgrowlocks 是 PostgreSQL 官方提供的擴展模塊&#xff0c;用于查看指定表中每一行當前的行級鎖&#xff08;Row Lock&#xff09;信息。它非常適用于&#xff1a; 并發沖突排查行級鎖等待分析死鎖前兆探測熱點數據行分析 二、安裝與啟用 1. 安裝前提&#xff08;…

關于xammp數據庫打開不了,但是日志沒錯誤的問題解決以及其數據庫的備份

這里參考了兩篇文章 解決Xampp中mysql無法啟動的問題_xampp里面mysql的stop啟動不起來-CSDN博客 mysqli_real_connect(): (HY000/1045): Access denied for user ‘root‘‘localhost‘ (using password: YES-CSDN博客 相信很多和我一樣&#xff0c;很久沒登xammp突然數據庫…

在UI 原型設計中,交互規則有哪些核心要素?

在UI 原型設計中&#xff0c;交互規則主要有三個核心要素&#xff0c;分別為重要性、原則與實踐&#xff0c;具體表現在&#xff1a; 一、交互規則在 UI 原型設計中的重要性 明確交互邏輯&#xff1a;設計階段制定交互規則&#xff0c;清晰定義界面元素操作響應。 如社交應用…

BFD與VRRP聯動

一、概述 在前面的文章我們學習了VRRP與BFD協議,VRRP(虛擬路由冗余協議)的主要特點是當Master(主)設備出現故障時,Backup(備用)設備能夠快速接替Master的轉發工作,盡量縮短數據流的中斷時間。 在沒有采用BFD與VRRP聯動機制前,當Master出現故障時,VRRP依靠Backup設置的超時時間來…

Protobuf3協議關鍵字詳解與應用實例

一、核心語法與基礎關鍵字 syntax 聲明協議版本&#xff0c;必須為文件的第一行非空、非注釋內容。 syntax "proto3"; // 顯式指定proto3語法&#xff0c;否則編譯器默認使用proto2message 定義消息類型&#xff0c;包含一組結構化字段。支持嵌套消息定義&#xff…

如何在線免費壓縮PDF文檔?

PDF文件太大&#xff0c;通常是因為內部嵌入字體和圖片。怎么才能將文件大小減減肥呢&#xff0c;主要有降低圖片清晰度和去除相關字體兩個方向來實現文檔效果。接下來介紹三個免費壓縮PDF實用工具。 &#xff08;一&#xff09;iLoveOFD在線轉換工具 iLoveOFD在線轉換工具&a…

NSSCTF [GFCTF 2021]where_is_shell

889.[GFCTF 2021]where_is_shell(system($0)64位) [GFCTF 2021]where_is_shell (1) 1.準備 motalymotaly-VMware-Virtual-Platform:~$ file shell shell: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.s…

深度學習中的提示詞優化:梯度下降全解析

深度學習中的提示詞優化:梯度下降全解析 在您的代碼中,提示詞的更新方向是通過梯度下降算法確定的,這是深度學習中最基本的優化方法。 一、梯度下降與更新方向 1. 核心公式 對于可訓練參數 θ \theta θ(這里是提示詞嵌入向量),梯度下降的更新公式為:

win10電腦無法訪問局域網內其他共享電腦文件的問題

一、啟用本地計算機guest來賓賬戶 操作步驟 點擊桌面上的“此電腦”圖標&#xff0c;再點擊“管理” 在“計算機管理”界面依次點擊“系統工具”“本地用戶和組”“用戶” 再點擊右側的“Guest”&#xff0c;在彈出的對話框中點擊“屬性” 在“Guest屬性”界面取消勾選“賬戶已…

會計要素+借貸分錄+會計科目+賬戶,幾個銀行會計的重要概念

1.借貸分錄還是借貸分路 正確表述是“借貸分錄”。 “分錄”即會計分錄&#xff0c;它是指預先確定每筆經濟業務所涉及的賬戶名稱&#xff0c;以及計入賬戶的方向和金額的一種記錄&#xff0c;簡稱分錄。 在借貸記賬法下&#xff0c;會計分錄通過“借”和“貸”來表示記賬方向…

AI日報 · 2025年5月15日|GPT-4.1 登陸 ChatGPT

AI日報 2025年5月15日&#xff5c;GPT-4.1 登陸 ChatGPT 1、OpenAI 在 ChatGPT 全面開放 GPT-4.1 與 GPT-4.1 mini 北京時間 5 月 14 日晚&#xff0c;OpenAI 在官方 Release Notes 中宣布&#xff1a;專為復雜代碼與精細指令場景打造的 GPT-4.1 正式加入 ChatGPT&#xff0…

π0: A Vision-Language-Action Flow Model for General Robot Control

TL;DR 2024 年 Physical Intelligence 發布的 VLA 模型 π0&#xff0c;基于 transformer 流匹配&#xff08;flow matching&#xff09;架構&#xff0c;當前開源領域最強的 VLA 模型之一。 Paper name π0: A Vision-Language-Action Flow Model for General Robot Contr…

Java詳解LeetCode 熱題 100(17):LeetCode 41. 缺失的第一個正數(First Missing Positive)詳解

文章目錄 1. 題目描述2. 理解題目3. 解法一&#xff1a;排序法&#xff08;不滿足題目要求&#xff09;3.1 思路3.2 Java代碼實現3.3 代碼詳解3.4 復雜度分析3.5 不足之處 4. 解法二&#xff1a;哈希表法4.1 思路4.2 Java代碼實現4.3 代碼詳解4.4 復雜度分析4.5 不足之處 5. 解…