C++初階-list的模擬實現(難度較高)

1.list(容器,不是類)模擬實現基本結構

這個實現是和源碼的實現不同的,不要進行強制類型轉換了,很麻煩。我們把定義放在.h文件中,在.cpp中放測試代碼。

注:這個基本結構之后會改變,到時候再講原因。

//.h
#include<iostream>
#include<list>
#include<assert.h>
using namespace std;
namespace td
{template<class T>struct list_node{public:list_node<T>* _next;list_node<T>* _prev;T _data;};template<class T>class list{public:typedef list_node<T> Node;private:Node* _head;};
}
//.cpp
#include"test.h"
namespace td
{ 
void test()
{ }
}
int main()
{return 0;
}

2.list的無參構造函數的實現

這個很簡單,我們直接先申請一個Node大小的空間,然后再改變指向即可:

//.h
list()
{_head = new Node;_head->_next = _head;_head->_prev = _head;
}

3.list_node的有參構造函數的實現

這個函數比較簡單,用初始化列表即可,但是,注意:我們這個構造有參是因為要把數據存儲起來,一般建議:給一個缺省值,這樣讓它更好的進行初始化。在C++中一般給T()即可:

//.h
list_node(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x)
{ }

4.list::push_back的實現

這個尾插難度不是很高,只要注意改變指向即可:

//.h
void push_back(const T& val)
{Node* newnode = new Node(x);Node* tail = _head->_prev;newnode->_prev = tail;tail->_next = newnode;newnode->_next = _head;_head->_prev = newnode;
}

這個代碼確實有些難看懂,主要是我們對雙向鏈表了解不是很全面,首先tail是尾結點,我們尾插是在尾結點之后插入結點,然后再把另外一些的指向改成正確的,這樣就沒太大的問題了。

5.list_iterator類的基本結構

我們無法用typedef Node* iterator的方式來得到迭代器,因為我們無法遍歷,如果std::list<int> lt1;std::list<int>::iterator it1=lt1.begin();其中如果*it1那么本質上可以理解*it1為:it1.operator*();而在這里面解引用是用_node->_data的方式實現的。所以我們需要用迭代器,這個迭代器是被封裝的一個類,那么基本結構為:

template<class T>
struct list_iterator
{typedef list_node<T> Node;Node* _node;
};

5.1list_iterator類的基本函數實現

一般包括但不限于*、++、--、==、!=這些運算符,還有->,這里一并實現了:

list_iterator(Node* node):_node(node)
{ }
T& operator*()
{return _node->data;
}
//前置++
list_iterator<T>& operator++()
{_node = _node->next;return *this;
}
bool operator!=(const list_iterator<T>& it)
{return _node != it._node;
}
bool operator==(const list_iterator<T>& it)
{return _node == it._node;
}

5.2list_iterator::operator->()的實現

這里先講它的寫法:

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

為什么能這樣寫?

假設:

struct A
{int _a1;int _a2;
};

如果在我們std::list<A> lt;auto it=lt.begin();如果it->_a1,

那么實際上它會轉化為it.operator->()->_a1也就是說,之前我們應當寫為:it->->_a1的,但是,為了可讀性,編譯器做了特殊處理,省略了一個->。所以能這樣寫。

6.如何實現list_const_iterator

我們無法在list_iterator里面加一個const迭代器版本,因為這樣并不能作為重載,因為返回值不同并不能作為重載,比如begin它在普通迭代器就要返回值為iterator,那么如果是const迭代器就要返回值為const_iterator,實際上就是:const T,所以我們只能自己寫一個額外的迭代器,那么我們可以這樣寫:

template<class T>
struct list_const_iterator
{typedef list_node<T> Node;Node* _node;list_const_iterator(Node* node):_node(node){}const T& operator*(){return _node->_data;}const T* operator->(){return &_node->_data;}list_const_iterator<T>& operator++(){_node = _node->_next;return *this;}bool operator!=(const list_const_iterator<T>& it){return _node != it._node;}bool operator==(const list_const_iterator<T>& it){return _node == it._node;}
};

我們發現大部分只是返回值不一樣而已,所以這種方式確實不是那么好,所以我們可以把它再寫為模板的形式。

7.list_iterator類的改進結構

為了適應多種迭代器,所以我們可以把它寫為模板形式,不過如果真再寫一個模板我們是沒有思路的,所以我們可以改成如下形式(先看一下,理解難度有些高):

template<class T,class Ref,class Ptr>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;list_iterator(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;}
};

這個結構就是改進后的結構,添加的兩個模板參數其中Ref意思是:引用,Ptr的意思是:指針。所以之后傳遞的時候就要這樣傳遞。其次,我們把之前寫為list_iterator的部分都改為Self。這里的++、--有后置與前置之分,所以我們要注意它的寫法,這里就不做過多講解了。

8.list類的改進結構

通過這些改變,我們可以加上這些:

typedef list_iterator<T, T&, T*> iterator;
typedef list_iterator<T, const T&,const T*> const_iterator;

9.list::begin、list::end的實現

這兩個函數都有const迭代器版本和普通迭代器版本,我們要注意的是,如果是begin,那么就是_head->_next,因為頭結點是哨兵位,不存儲數據;如果是end那么就是哨兵位,因為end這個位置是不存儲數據的,可以認為這是尾結點的后面一個指針!!!所以我們可以寫出:

//list
iterator begin()
{return _head->_next;
}
iterator end()
{return _head;
}
const_iterator begin() const
{return _head->_next;
}
const_iterator end() const
{return _head;
}

10.list的增刪查改的實現

這些增刪查改相對于我們之前的數據結構中的雙向鏈表沒什么太大的區別,如果想要更加深入了解代碼生成的原因,可以看我之前的博客:

數據結構初階-雙向鏈表-CSDN博客文章瀏覽閱讀524次,點贊9次,收藏7次。為結構體,其中包含一個指針指向下一個結點的結構體指針next,一個指針指向上一個結點的指針prev,還有所存儲的數據data,由于存儲的數據類型未知,所以我們typedef int LTDataType;若結果為8 -> 6 -> 4 -> 2 -> 3 -> 1 -> 2 -> 3 -> 4 ->則代碼無誤,如果感興趣的話也可以寫一個在pos位置之前插入數據的函數。如果結果為8 -> 6 -> 4 -> 2 -> 3 -> 1 -> 2 -> 3 -> 4 ->//存儲下一個結點的地址。 https://blog.csdn.net/2401_86446710/article/details/145201866?sharetype=blogdetail&sharerId=145201866&sharerefer=PC&sharesource=2401_86446710&sharefrom=mp_from_link那么這些東西比較簡單,我這里就只講解一些比較重要的代碼:

//list
//在pos位置之前插入
void 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;
}
//清除pos位置的數據,并返回清除后該位置的指針
iterator erase(iterator pos)
{//防止鏈表為空assert(pos != end());Node* cur = pos._node;Node* nextNode = cur->_next;Node* prevNode = cur->_prev;//只要改變兩個指針的指向即可prevNode->_next = nextNode;nextNode->_prev = prevNode;delete cur;//強制類型轉換return iterator(nextNode);
}
void push_back(const T& x)
{insert(end(), x);
}
void push_front(const T& x)
{insert(begin(), x);
}
void pop_front()
{erase(begin());
}
void pop_back()
{//end位置沒有數據,所以要清除end位置前面一個位置的數據erase(--end());
}

11.list::size的實現

這個函數的實現可能需要先加一個_size到成員變量里面,所以可以寫出:

//list
template<class T>
class list
{
public:typedef list_node<T> Node;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&,const T*> const_iterator;list(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}//void push_back(const T& val)//{//	Node* newnode = new Node(x);//	Node* tail = _head->_prev;//	newnode->_prev = tail;//	tail->_next = newnode;//	newnode->_next = _head;//	_head->_prev = newnode;//}iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}//在pos位置之前插入void 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;++_size;}//清除pos位置的數據,并返回清除后該位置的指針iterator erase(iterator pos){//防止鏈表為空assert(pos != end());Node* cur = pos._node;Node* nextNode = cur->_next;Node* prevNode = cur->_prev;//只要改變兩個指針的指向即可prevNode->_next = nextNode;nextNode->_prev = prevNode;delete cur;--_size;//強制類型轉換return iterator(nextNode);}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_front(){erase(begin());}void pop_back(){//end位置沒有數據,所以要清除end位置前面一個位置的數據erase(--end());}size_t size() const{return _size;}
private:Node* _head;size_t size;
};

12.list::clear的實現

這個函數我們可以知道,直接先erase后返回值是有效的,所以我們不用怕it是野指針,所以我們可以這樣寫:

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

由于erase已經delete了pos位置的結點(原it位置),所以直接這樣寫就可以了。

13.list::~list的實現

這個函數只要先調用clear函數后再把哨兵位的空間釋放,_size置為0即可:

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

14.list::swap的實現

這個函數實現不是很難,所以直接寫了:

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}

15.list的拷貝構造函數的實現

我們和list底層相似的方式進行完善:

void empty_init()
{//哨兵位_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list(const list<T>& lt)
{empty_init();for (auto e : lt){push_back(e);}
}

16.list::operator=的實現

這個函數也是比較簡單的實現,但是要注意:我們不能給形參加上引用,因為那樣是比較麻煩的寫法:

list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

17.list::list(initializer_list<T> lt)的實現

這個函數實現不是很難,我們直接先構造一個頭結點,再和拷貝構造一樣尾插即可:

list(initializer_list<T> lt)
{empty_init();for (auto& e : lt){push_back(e);}
}

18.測試

我們用以下代碼進行測試:

namespace td
{void test(){td::list<int> l1 = { 5,6,3,6,1,9 };td::list<int> l2 = l1;td::list<int> l3(l1);td::list<int> l4;l4 = l1;for (const auto& e : l1){cout << e << " ";}cout << endl;for (const auto& e : l2){cout << e << " ";}cout << endl;for (const auto& e : l3){cout << e << " ";}cout << endl;for (const auto& e : l4){cout << e << " ";}cout << endl;l1.push_back(3);l2.pop_back();l3.push_front(8);l4.pop_front();for (const auto& e : l1){cout << e << " ";}cout << endl;for (const auto& e : l2){cout << e << " ";}cout << endl;for (const auto& e : l3){cout << e << " ";}cout << endl;for (const auto& e : l4){cout << e << " ";}cout << endl;}
}
//struct A
//{
//	int _a1;
//	int _a2;
//};
int main()
{td::test();return 0;
}

那么最終運行結果為:

如果有人想測試一下insert和erase,可以用一個迭代器通過begin()和end()進行++、--的操作,進行插入刪除測試即可。

19.總結

list的模擬實現已經全部講解完,有些不常用的函數如:remove_if、splice這些感興趣的可以自己去實現一下。std::list已經全部講完了,下講將講解:stack和queue(棧和隊列)。喜歡的可以一鍵三連哦,下講再見!

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

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

相關文章

【ROS】Nav2源碼之nav2_behavior_tree-行為樹節點列表

1、行為樹節點分類 在 Nav2(Navigation2)的行為樹框架中,行為樹節點插件按照功能分為 Action(動作節點)、Condition(條件節點)、Control(控制節點) 和 Decorator(裝飾節點) 四類。 1.1 動作節點 Action 執行具體的機器人操作或任務,直接與硬件、傳感器或外部系統…

【iOS】cell的復用以及自定義cell

【iOS】cell的復用以及自定義cell 文章目錄 【iOS】cell的復用以及自定義cell前言cell的復用手動&#xff08;非注冊&#xff09;自動&#xff08;注冊&#xff09; 自定義cell 前言 cell的復用及自定義cell是UITableView或UICollectionView的一個重要優化機制,當用戶滾動視圖…

深度學習之模型壓縮三駕馬車:基于ResNet18的模型剪枝實戰(2)

前言 《深度學習之模型壓縮三駕馬車&#xff1a;基于ResNet18的模型剪枝實戰&#xff08;1&#xff09;》里面我只是提到了對conv1層進行剪枝&#xff0c;只是為了驗證這個剪枝的整個過程&#xff0c;但是后面也有提到&#xff1a;僅裁剪 conv1層的影響極大&#xff0c;原因如…

傳輸層協議:UDP

目錄 1、概念 2、報文結構 3、核心特性 3.1 無連接 3.2 不可靠交付 3.3 面向數據報 3.4 輕量級&高效 3.5 支持廣播和組播 4、典型應用場景 5、優缺點分析 6、與TCP的區別 1、概念 UDP&#xff08;User Datagram Protocol&#xff0c;用戶數據報協議&#xff09…

JVM虛擬機:內存結構、垃圾回收、性能優化

1、JVM虛擬機的簡介 Java 虛擬機(Java Virtual Machine 簡稱:JVM)是運行所有 Java 程序的抽象計算機,是 Java 語言的運行環境,實現了 Java 程序的跨平臺特性。JVM 屏蔽了與具體操作系統平臺相關的信息,使得 Java 程序只需生成在 JVM 上運行的目標代碼(字節碼),就可以…

c++ 面試題(1)-----深度優先搜索(DFS)實現

操作系統&#xff1a;ubuntu22.04 IDE:Visual Studio Code 編程語言&#xff1a;C11 題目描述 地上有一個 m 行 n 列的方格&#xff0c;從坐標 [0,0] 起始。一個機器人可以從某一格移動到上下左右四個格子&#xff0c;但不能進入行坐標和列坐標的數位之和大于 k 的格子。 例…

【匯編逆向系列】七、函數調用包含多個參數之浮點型- XMM0-3寄存器

目錄 1. 匯編代碼 1.1 debug編譯 1.2 release編譯 2. 匯編分析 2.1 浮點參數傳遞規則 2.2 棧幀rsp的變化時序 2.3 參數的訪問邏輯 2.4 返回值XMM0寄存器 3. 匯編轉化 3.1 Debug編譯 3.2 Release 編譯 3.3 C語言轉化 1. 匯編代碼 上一節介紹了整型的函數傳參&#x…

華為云Flexus+DeepSeek征文 | 從零到一:用Flexus云服務打造低延遲聯網搜索Agent

作者簡介 我是摘星&#xff0c;一名專注于云計算和AI技術的開發者。本次通過華為云MaaS平臺體驗DeepSeek系列模型&#xff0c;將實際使用經驗分享給大家&#xff0c;希望能幫助開發者快速掌握華為云AI服務的核心能力。 目錄 作者簡介 前言 1. 項目背景與技術選型 1.1 項目…

【多智能體】受木偶戲啟發實現多智能體協作編排

&#x1f60a;你好&#xff0c;我是小航&#xff0c;一個正在變禿、變強的文藝傾年。 &#x1f514;本專欄《人工智能》旨在記錄最新的科研前沿&#xff0c;包括大模型、具身智能、智能體等相關領域&#xff0c;期待與你一同探索、學習、進步&#xff0c;一起卷起來叭&#xff…

Java八股文——Spring篇

文章目錄 Java八股文專欄其它文章Java八股文——Spring篇SpringSpring的IoC和AOPSpring IoC實現機制Spring AOP實現機制 動態代理JDK ProxyCGLIBByteBuddy Spring框架中的單例Bean是線程安全的嗎&#xff1f;什么是AOP&#xff0c;你們項目中有沒有使用到AOPSpring中的事務是如…

NineData數據庫DevOps功能全面支持百度智能云向量數據庫 VectorDB,助力企業 AI 應用高效落地

NineData 的數據庫 DevOps 解決方案已完成對百度智能云向量數據庫 VectorDB 的全鏈路適配&#xff0c;成為國內首批提供 VectorDB 原生操作能力的服務商。此次合作聚焦 AI 開發核心場景&#xff0c;通過標準化 SQL 工作臺與細粒度權限管控兩大能力&#xff0c;助力企業安全高效…

開源技術驅動下的上市公司財務主數據管理實踐

開源技術驅動下的上市公司財務主數據管理實踐 —— 以人造板制造業為例 引言&#xff1a;財務主數據的戰略價值與行業挑戰 在資本市場監管日益嚴格與企業數字化轉型的雙重驅動下&#xff0c;財務主數據已成為上市公司財務治理的核心基礎設施。對于人造板制造業而言&#xff0…

借助它,普轉也能獲得空轉信息?

在生命科學研究領域&#xff0c;轉錄組技術是探索基因表達奧秘的有力工具&#xff0c;在疾病機制探索、生物發育進程解析等諸多方面取得了顯著進展。然而&#xff0c;隨著研究的深入&#xff0c;研究人員發現普通轉錄組只能提供整體樣本中的基因表達水平信息&#xff0c;卻無法…

synchronized 學習

學習源&#xff1a; https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.應用場景 不超賣&#xff0c;也要考慮性能問題&#xff08;場景&#xff09; 2.常見面試問題&#xff1a; sync出…

Java事務回滾詳解

一、什么是事務回滾&#xff1f; 事務回滾指的是&#xff1a;當執行過程中發生異常時&#xff0c;之前對數據庫所做的更改全部撤銷&#xff0c;數據庫狀態恢復到事務開始前的狀態。這是數據庫“原子性”原則的體現。 二、Spring 中的 Transactional 默認行為 在 Spring 中&am…

云災備數據復制技術研究

云災備數據復制技術&#xff1a;數字時代的“安全氣囊” 在當今信息化時代&#xff0c;數據就像城市的“生命線”&#xff0c;一旦中斷&#xff0c;后果不堪設想。想象一下&#xff0c;如果政務系統突然崩潰&#xff0c;成千上萬的市民服務將陷入癱瘓。這就是云災備技術的重要…

如何處理Shopify主題的顯示問題:實用排查與修復指南

在Shopify店鋪運營過程中&#xff0c;主題顯示問題是影響用戶體驗與品牌形象的常見痛點。可能是字體錯位、圖片無法加載、移動端顯示混亂、功能失效等&#xff0c;這些都可能造成客戶流失和轉化下降。 本文將從問題識別、原因分析、修復方法到開發者建議全方位解讀如何高效解決…

前端監控方案詳解

一、前端監控方案是什么&#xff1f; 前端監控方案是一套系統化的工具和流程&#xff0c;用于收集、分析和報告網站或Web應用在前端運行時的各種性能指標、錯誤日志、用戶行為等數據。它通常包括以下幾個核心模塊&#xff1a; 性能監控&#xff1a;頁面加載時間、資源加載時間…

Camera相機人臉識別系列專題分析之十二:人臉特征檢測FFD算法之libvega_face.so數據結構詳解

【關注我&#xff0c;后續持續新增專題博文&#xff0c;謝謝&#xff01;&#xff01;&#xff01;】 上一篇我們講了&#xff1a; Camera相機人臉識別系列專題分析之十一&#xff1a;人臉特征檢測FFD算法之低功耗libvega_face.so人臉屬性(年齡&#xff0c;性別&#xff0c;膚…

如何配置HarmonyOS 5與React Native的開發環境?

配置 HarmonyOS 5 與 React Native 的開發環境需遵循以下步驟 一、基礎工具安裝 ?DevEco Studio 5.0? 從 HarmonyOS 開發者官網 下載安裝勾選組件&#xff1a; HarmonyOS SDK (API 12)ArkTS 編譯器JS/ArkTS 調試工具HarmonyOS 本地模擬器 ?Node.js 18.17 # 安裝后驗證版…