STL源碼刨析:序列式容器之list

目錄

????????1.前言

????????2.list的節點定義和結構

????????3.list的迭代器定義和結構

????????4.list的定義和結構

????????5.list的內存管理????????

????????6.list的元素操作


? ? ? ? 前言

? ? ? ? 在刨析了vector容器的源碼后,list容器相比與vector容器,其元素的插入和刪除較快,不需要對原本容器中的元素進行排序,因此針對list容器的每一次插入和刪除都是常數級。而且list對于內存的申請屬于是需要幾個內存就申請幾個內容,而vector申請的內存通常為當前內存的兩倍。本章將對list進行講解,至于何時使用vector,何時使用list更是要區別于元素的多少,具體的業務場景。


list的節點定義和結構

? ? ? ? list的結構屬于環狀雙向鏈表的結構(PS:環狀雙向鏈表,指的是鏈表的頭指針指向尾節點,鏈表的尾指針指向頭節點,故稱環狀雙向鏈表),應此在實現list時,我們還需要針對其list中的每一個節點進行設計。在設計list節點時,要實現list雙向鏈表的特性,我們還需要兩個指針,一個頭指針一個尾指針,且每一個節點需要存儲值,故list節點設計代碼如下:

//list節點設計代碼
template <class T>
struct _list_node{typedef void* void_pointer;//設計為空指針方便類型轉換,也可以設計為_list_node<T>*void_pointer prev;//頭指針void_pointer next;//尾指針T data;           //存儲值
}

list的迭代器定義和結構

????????相比于vector的隨機迭代器,list提供的迭代器為雙向迭代器,因為list不支持下標操作。而且vector在擴充元素時,若內存空間不足則需要向系統申請新的空間,并把舊元素復制到新的空間上,這會導致迭代器生效(類似于虛吊指針,也可以叫懸空指針)。但是list的插入操作都只是修改節點的頭指針和尾指針的指向空間,應此不會導致迭代器生效。在了解list迭代器類型后,其設計的源碼如下:

//list的迭代器設計源碼
template<class T, class Ref, class Ptr>    //Ref代表解引用時返回引用類型,Ptr則是指針類型
struct _list_iterator{typedef _list_node<T>* link_type;    /list節點指針link_type node;    //變量node指向list節點typedef _List_iterator<T, Ref, Ptr> self;      //當前實例對象的別名typedef _list_iterator<T, T&,T*> iterator;    //當前實例對象的迭代器typedef bidirectional_iterator_tag iterator_category;    //封裝雙向迭代器//以下為迭代器常用封裝,Traits編程方法typedef T value_tyoe;typedef Ptr pointer;typedef Ref reference;typedef size_t size_type;typedef ptrdiff_t difference_type;
}

? ? ? ? 在知道list迭代器為雙向迭代器后,我們知道雙向迭代器的特點,即不支持下標操作,僅支持迭代器的累加,遞減,解引用和判斷節點值是否相等的特性,為了實現這些特性,其迭代器還需要實現以下功能:

//list迭代器支持的操作的實現
_list_iterator(){}
_list_iterator(link_type x) : node(x){}
_list_iterator(const iterator& x) : node(x.node){}bool operator==(const self& x) const {    //判斷是否相等return node == x.node;
}bool operator!=(const self& x) const {    //判斷是否不等return node != x.node;
}pointer operator->() const { return &(operator*()); } //取值(左值)reference operator*() const { return (*node).data; }    //取值(右值)//以下為累加的實現
self& operator++(){node = (link_type)((*node).next);return *this;
}self& operator++(int){self temp =*this;++(*this);return tem;
}//以下為遞減的實現
self& operator--(){node = (link_type)((*node).prev);return *this;
}self& operator--(int){self tmp = *this;--(*this);return tmp;
}

list的定義和結構

? ? ? ? 在了解list節點和迭代器的定義和結構后,我們還提到list是一種環狀的雙向鏈表,而由于獨特的環狀結構,我們在設計該雙鏈表時需要插入一個空的節點(PS:是為了滿足STL的前閉后開的要求,但是我覺得更多的是為了方便判斷是否遍歷完整個鏈表而設計的節點)。在滿足設計的結構的基礎上,我們在實現list的結構時,還需要定義形如begin(),end(),empty(),size(),font()和back()等操作函數,故list結構大致如下:

//list的結構實現
template<class T, class Alloc = alloc>
class list{
protected:typedef _list_node<T> list_node;typedef simple_alloca<list_node,Alloc> list_node_allocator;//封裝迭代器,內存管理中使用
public:typedef list_node* link_type;
protected:link_type node;    //節點指針,指向鏈表的頭節點便可表示整個鏈表
}iterator begin() { return (link_type)((*node).next); }    //獲取頭節點iterator end() { return node; }    //獲取尾節點bool empty() const { return node->next == node; }    //判斷是否為空節點size_type size() const{    //計算容器中元素的個數size_type result = 0;    distance(begin(), end(), result);//遍歷容器return result;    
}reference front() { return *begin(); }    //取頭節點的值rederence back() { return *(--end()); }   //取尾節點的值

? ? ? ? 針對list的環狀雙向鏈表,可以參考下圖:

圖1.listD的環狀雙向鏈表結構圖


list的內存管理????????

? ? ? ? 參考vector容器的講解,其list內部也存在應該迭代器,為了實現內存的精準控制,其迭代器也是專門定義了一個(參考小節:list的迭代器定義和結構),為了實現內存的管理,我們需要滿足內存的分配,釋放等操作,在這些操作基礎上還需要滿足帶值的節點的內存申請,對此list于內存相關的代碼如下:

//list內存管理源碼實現
link_type get_node(){ return list_node_allocator::allocate(); }   //申請一個節點link_type put_node(link_type p){ return list_node_allocator::deallocate(p); }//釋放一個節點link_type create_node(const T& x){    //申請一個帶值的節點link_type p = get_node();construct(&p->data,x);    //構造函數return p;
}link_type destroy_node(link_type p){  //銷毀一個帶值的節點destroy(&p->data);put_node(p);
}list() { empty_initialize(); }    //list構造函數,用于產生一個空鏈表void empty_initialize(){    //產生一個空節點node = get_node();node->next = node;noed->prev = node;
}

list的元素操作

? ? ? ? 形如vector,list也提供了許多元素操作的函數,如insert(),push_back(),erase()和uniques()函數等操作,本小節將對這些元素操作進行源碼的講解,如下:

? ? ? ? 1.insert()函數實現源碼

//inser()用于在指定位置前插入元素
iterator inser(iterator position, const T& x){link_type tmp = create_node(x);    //初始化帶值節點//調整當前插入節點的指針指向tmp->next = position.node;tmp->prev = position.node->prev;//調整迭代器指向節點的指針指向(link_type(position.node->prev))->next = tmp;position.node->prev = tmp;return tmp;
}

? ? ? ? 2.push_front()函數實現源碼

//push_front()用于插入一個節點作為頭節點
void push_front(const T& x){insert(begin(),x);
}

? ? ? ? 3.push_back()函數實現源碼

//push_back()函數用于插入一個節點作為尾節點
void push_back(const T& x){insert(end(),x);
}

? ? ? ? 4.erase()函數實現源碼

//erase()函數用于移除指定節點
iterator erase(iterator position){link_type next_node = link_type(position.node->next);    //獲取頭指針指向的節點link_type prev_node = link_type(position.node->prev);    //獲取尾指針指向的節點//更新移除節點的前后節點指針的指向prev_node->next = next_node;next_node->prev = prev_node;destory_node(position.node);   //釋放當前節點return iterator(next_node);    //返回移除節點的上一個節點指針
}

? ? ? ? 5.pop_front()函數實現源碼

//pop_front()函數用于移除頭節點
void pop_front(){rease(begin());
}

? ? ? ? 6.pop_back()函數實現源碼

//pop_back()函數用于移除尾節點
void pop_back(){iterator tmp = end();rease(--tmp);
}//因為真實的尾節點其值為空,參考環狀雙向鏈表結構體
//所以先獲取尾節點后,再移動指針指向帶有值的最后一個節點,最后釋放該節點

? ? ? ? 7.clear()函數實現源碼

//clear()函數用于清除所有節點
template<class T , class Alloc>
void list<T,Alloc>::clear(){link_type cur = (link_type) node->next;    //獲取鏈表頭節點while(cur != node){    //遍歷鏈表中的節點link_type tmp = cur;        cur = (link_type) cur->next;//更新為下一個節點destroy_node(tmp);}//恢復node狀態node->next = node;node->prev = node;
}

? ? ? ? 8.remove()函數實現源碼

//remove()函數用于移除指定值的所有節點
template<class T, class Alloc>
void list<T,Alloc>::remove(const T& value){iterator first = begin();    //獲取頭節點iterator lase = end();       //獲取尾節點while(first != lase){    //遍歷所有節點iterator next = first;++next;    //獲取下一個節點if(*first == value) erase(first);first = next;}
}

? ? ? ? 9.unique()函數實現源碼

//unique()函數用于移除數值相同的連續元素(最后剩余一個)
template<class T, class Alloc>
void list<T,Alloc>::unique(){iterator first = begin();iterator lase = end();if(first == last) teturn;    //空鏈表則退出iterator next = first;while(++next != last){    //遍歷所有節點if(*first == *next)    erase(next);elsefirst = next;next = first;}
}

? ? ? ? 10.transfer()函數實現源碼

//transfer()函數用于將指定范圍內的節點移動到指定節點的前面
void transfer(iterator position, iterator first, iterator last){//position:指定節點    first:范圍的頭節點    last:范圍的尾節點(不包含至移動的范圍中)if(position != last){(*(link_type((*last.node).prev))).next = position.node;(*(link_type((*first.node).prev))).next = last.node;(*(link_type((*position.node).prev))).next = first.node;link_type tmp = link_type((*position.node).prev);(*position.node).prev = (*last.node).prev;(*last.node).prev = (*first.node).prev;(*first.node).prev = tmp;}
}

????????11.splice()函數實現源碼

//splice()函數用于將指定的兩個鏈表結合
void splice(iterator position, list& x){if(!x.empty()){    //判斷鏈表是否為空transfer(position,x.begin(),x.end());}
}void splice(iterator position, list& x, iterator i){iterator j = i;++j;if(position == i || position == j) return;    //當前鏈表只存在一個節點transfer(position,i,j);
}void splice(terator position, list& x, iterator first, iterator last){if(first != last)  transfer(position,first,last)
}

? ? ? ? 11.merge()函數實現源碼

//merge()函數用于合并兩個遞增的鏈表,最后鏈表元素排序也為遞增
template<class T, class Alloc>
void list<T,Alloc>::merge(list<T,Alloc>& x){iterator first1 = begin();iterator last1 = end();iterator first2 = x.begin();iterator last2 = x.end();while(first1 != last1 && first2 != last2){    //遍歷次數為最短的鏈表元素個數if(*first2 < *first1){    //當鏈表1的值大于鏈表2iterator next = first2;transfer(first1,first2,++next);first2 = next;}else{++first1;}if(first2 != last2) { transfer(last,first1,first2); }    //如果鏈表沒有插入完,則合并}
}

? ? ? ? 12.reverse()函數實現源碼

//reverse()函數用于見元素逆向排序
template<class T, class Alloc>
void list<T,Alloc>::reversr(){if(node->next == node || link_type(node->next)->next == node) return; //鏈表節點為1或0iterator first = begin();++first;while(first != end()){    遍歷整個鏈表iterator old = first;++first;transfer(begin(),old,first);    //把頭節點到old節點轉移到鏈表前}
}

? ? ? ? 13.sort()函數實現源碼

//sort()函數用于對鏈表進行排序
template <class T, class Alloc>
void list<T, Alloc>::sort() {if(node->next == node || link_type(node->next)->next == node) return;//鏈表節點個數為0或1list<T, Alloc> carry;        //臨時存儲鏈表節點list<T, Alloc> counter[64];int fill = 0;while (!empty()) {carry.splice(carry.begin(), *this, begin());int i = 0;while(i < fill && !counter[i].empty()) {counter[i].merge(carry);carry.swap(counter[i++]);}carry.swap(counter[i]);         if (i == fill) ++fill;} for (int i = 1; i < fill; ++i) counter[i].merge(counter[i-1]);swap(counter[fill-1]);
}

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

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

相關文章

[9] CUDA性能測量與錯誤處理

CUDA性能測量與錯誤處理 討論如何通過CUDA事件來測量它的性能如何通過CUDA代碼進行調試 1.測量CUDA程序的性能 1.1 CUDA事件 CPU端的計時器可能無法給出正確的內核執行時間CUDA事件等于是在你的CUDA應用運行的特定時刻被記錄的時間戳&#xff0c;通過使用CUDA事件API&#…

UVa1466/LA4849 String Phone

UVa1466/LA4849 String Phone 題目鏈接題意分析AC 代碼 題目鏈接 本題是2010年icpc亞洲區域賽大田賽區的G題 題意 平面網格上有n&#xff08;n≤3000&#xff09;個單元格&#xff0c;各代表一個重要的建筑物。為了保證建筑物的安全&#xff0c;警察署給每個建筑物派了一名警察…

MFC 用Imm類庫實現輸入法修改輸入模式

1.導入Imm類庫&#xff0c;電腦里都有 #include <Imm.h> #pragma comment(lib, "imm32.lib")2.在想要的地方增加代碼 HIMC himc ImmGetContext(m_hWnd);if (himc ! NULL) {ImmSetOpenStatus(himc, TRUE);ImmNotifyIME(himc, NI_COMPOSITIONSTR, CPS_CANCEL,…

時代終結,微軟宣布淘汰VBScript;Flink漏洞被廣泛利用;Grandoreiro銀行木馬強勢回歸,1500多家銀行成攻擊目標 | 安全周報0524

揭秘SolarMarker惡意軟件&#xff1a;多層次基礎設施讓清除工作陷入困境 Recorded Future的新發現表明&#xff0c;SolarMarker信息竊取惡意軟件背后的持續威脅行為者已經建立了一個多層次的基礎設施&#xff0c;以使執法部門的清除工作變得復雜。 該公司在上周發布的一份報告…

SwiftUI中AppStorage的介紹使用

在Swift中&#xff0c;AppStorage是SwiftUI中引入的一個屬性包裝器&#xff0c;在這之前我們要存儲一些輕量級的數據采用UserDefaults進行存取。而AppStorage用于從UserDefaults中讀取值&#xff0c;當值改變時&#xff0c;它會自動重新調用視圖的body屬性。也就是說&#xff0…

React@16.x(11)ref

目錄 1&#xff0c;介紹1.1&#xff0c;得到的結果 2&#xff0c;參數類型2.1&#xff0c;字符串&#xff08;不再推薦&#xff09;2.2&#xff0c;對象2.3&#xff0c;函數函數調用時機 3&#xff0c;注意點 1&#xff0c;介紹 reference 引用。和 vue 中的 refs 類似&#x…

IEC60870-5-104通信規約 | 報文解析 | 組織報文與解析報文(C++)

文章目錄 一、IEC60870-5-104通信規約1.IEC104的報文結構2.IEC104的報文格式--I/U/S格式2.1 I幀2.2 U幀2.3 S幀 3.應用服務數據單元ASDU 二、IEC60870-5-104規約通信過程報文幀解析三、組織報文與解析報文&#xff08;C&#xff09; 一、IEC60870-5-104通信規約 IEC60870-5-104…

golang 守護進程管理

添加守護進程 vim /etc/systemd/system/xxx.service [Unit] DescriptionGo Socket Service Afternetwork.target[Service] Typesimple ExecStart/data/quwan/quwan_ws WorkingDirectory/data/quwan # 停止前發送信號 ExecStop/bin/kill -SIGTERM $MAINPID # 如果超過20s 進程…

筆記-Python lambda

在學習python的過程中&#xff0c;lambda的語法時常會使人感到困惑&#xff0c;lambda是什么&#xff0c;為什么要使用lambda&#xff0c;是不是必須使用lambda&#xff1f; 下面就上面的問題進行一下解答。 1、lambda是什么&#xff1f; 看個例子&#xff1a; 1 g lambda…

什么是GPT-4o,推薦GPT-4o的獲取使用方法,使用GPT4o模型的最新方法教程(2024年5月16更新)

2024年5月最新GPT-4o模型使用教程和簡介 2024年5月最新GPT-4o模型使用教程和簡介 2024 年 5 月 13 日&#xff0c;openai 發布了最新的模型 GPT4o。 很多同學還不知道如何訪問GPT-4、GPT-4 Turbo和GPT-4o等模型&#xff0c;這篇文章介紹如何在ChatGPT中訪問GPT-4o&#xff0…

milvus索引

Milvus是一個開源的向量數據庫引擎&#xff0c;旨在支持大規模向量相似度搜索和分析。索引在Milvus中扮演著非常重要的角色&#xff0c;它們用于加速向量數據的檢索。下面詳細介紹一下Milvus中的索引&#xff1a; 1. 索引類型 Milvus支持多種索引類型&#xff0c;每種類型都適…

無人機偵察:雷達系統概述

一、雷達基本原理 無人機偵察中的雷達系統主要基于無線電波的傳播和反射原理。雷達發射機產生特定頻率的電磁波&#xff0c;并通過天線以定向波束形式向空間發射。當這些電磁波遇到目標時&#xff0c;部分能量會被反射回來&#xff0c;被雷達接收機捕獲。通過測量發射和接收電…

基于SpringBoot+Vue+Redis+Mybatis的商城購物系統 【系統實現+系統源碼+答辯PPT】

前言 該系統采用SpringBootVue前后端分離開發&#xff0c;前端是一個單獨的項目&#xff0c;后端是一個單獨的項目。 ??技術棧&#xff1a;SpringBootVueMybatisRedisMysql ??開發工具&#xff1a;IDEA、Vscode ??瀏覽器&#xff1a;Chrome ??開發環境&#xff1a;JDK1…

Pytorch 筆記

執行下面這段代碼后&#xff0c;為什么返回的是 2 &#xff1f; vector torch.tensor([7, 7]) vector.shape為什么返回的是 torch.Size([2])&#xff1f; 當你創建一個PyTorch張量時&#xff0c;它會記住張量中元素的數量和每個維度的大小。在你的代碼中&#xff0c;torch.t…

通過 js 調起微信官方的微信支付api

通過 js 調起微信官方的微信支付api function onBridgeReady() {WeixinJSBridge.invoke(getBrandWCPayRequest, { "appId": "wx2421b1c4370ec43b", // 公眾號ID&#xff0c;由商戶傳入 "timeStamp": "1395712654", // 時間戳&quo…

動態插入HTML內容有哪些常見用法

動態插入HTML內容的常見用法包括但不限于以下幾種情況&#xff1a; 用戶交互反饋&#xff1a;當用戶在網頁上進行某些操作時&#xff08;如點擊按鈕、提交表單等&#xff09;&#xff0c;可以使用JavaScript動態插入HTML內容來提供即時的反饋或結果。例如&#xff0c;當用戶點…

vue3第三十五節(TS 之 泛型)

本節介紹 ts 中泛型的常用情景 1 什么是泛型 泛型的本質是參數化類型&#xff0c;也就是說所操作的數據類型被指定為一個參數。這種參數類型可以用在類、接口和方法的創建中&#xff0c;分別稱為泛型類、泛型接口、泛型方法。 泛型使用<T>來定義類型&#xff0c;<T…

使用canarytokens進行入侵檢測

canarytokens 基本概念 canarytokens是一種用于識別網絡入侵的工具。它們是一種虛擬的“蜜罐”&#xff0c;可以在網絡上放置&#xff0c;當有人嘗試訪問它們時&#xff0c;可以立即觸發警報&#xff0c;以便及時發現潛在的安全威脅。這些token可以是各種形式&#xff0c;可以…

項目管理基礎知識

項目管理基礎知識 導航 文章目錄 項目管理基礎知識導航一、項目相關概念二、時間管理三、人員管理四、風險管理 一、項目相關概念 項目定義的三層意思 一定的資源約束:時間資源、經費資源、人力資源一定的目標一次性任務 里程碑 是項目中的重要時點或事件持續時間為零&…

深度神經網絡——什么是遷移學習?

1.概述 在練習機器學習時&#xff0c;訓練模型可能需要很長時間。從頭開始創建模型架構、訓練模型&#xff0c;然后調整模型需要大量的時間和精力。訓練機器學習模型的一種更有效的方法是使用已經定義的架構&#xff0c;可能具有已經計算出的權重。這是背后的主要思想 遷移學習…