C++:vector增刪查改模擬實現

C++:vector增刪查改模擬實現

  • 前言
  • 一、迭代器
    • 1.1 非const迭代器:begin()、end()
    • 1.2 const迭代器:begin()、end()
  • 二、構造函數、拷貝構造函數、賦值重載、析構函數模擬實現
    • 2.1 構造函數
      • 2.1.1 無參構造
      • 2.1.2 迭代器區間構造
      • 2.1.3 n個值構造
    • 2.2 拷貝構造
    • 2.3 賦值重載
    • 3 析構函數
  • 三、容量相關:capacity()、size()、reserve()、resize()
    • 3.1 capacity()
    • 3.2 size()
    • 3.3 reserve()
    • 3.4 resize()
  • 四、operator[ ]重載
  • 五、元素相關:insert、erase、push_back、pop_back
    • 5.1 insert()
    • 5.2 erase()
      • 5.2.1 erase迭代器失效
    • 5.3 push_bach()
    • 5.4 pop_back()
  • 六、所有代碼


前言

提前在這說明下,vector增刪查改模擬實現的成員變量博主采用SGI版本。下面給出其庫中成員變量是哪些,后續的模擬實現都基于此。

在這里插入圖片描述
我們發現庫中定義了3個T*的變量。同時3個成員變量的意義如下:

在這里插入圖片描述

一、迭代器

1.1 非const迭代器:begin()、end()

typedef T* iterator;iterator begin()
{return _start;
}iterator end()
{return _finish;
}

1.2 const迭代器:begin()、end()

typedef const T* const_iterator;const_iterator begin()const
{return _start;
}const_iterator end()const
{return _finish;
}

二、構造函數、拷貝構造函數、賦值重載、析構函數模擬實現

2.1 構造函數

我們先來看看vector庫中的構造類型如下:
在這里插入圖片描述
我們知道有三種構造方式,下面給出各種的實現方式。

2.1.1 無參構造

vector():_start(nullptr),_finish(nullptr), _endofstorage{}

2.1.2 迭代器區間構造

template<class InputIterator>//使用模板是為了,當數據類型匹配時就可以使用
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);first++;}
}

2.1.3 n個值構造

vector(size_t  n, const T& value = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(value);}
}//防止定義vector<int>這種類型走迭代區間的構造函數,我們在多實現一個以下類型函數
//當使用vector<int>類型的去構造時,此時調用的構造函數兩個參數都是int。所有他會走最匹配的函數,即迭代器區間構造生成的構造函數,程序會出錯。
//而下面給出了現成的最匹配構造函數,編譯器調用時就不會走模板。
vector(int n, const T& value = T())
{reserve(n);for (size_t i = 0; i < n; i++){push_back(value);}
}

2.2 拷貝構造

拷貝構造我們先調用開好一塊空間,在依次插入數據即可。

//vector(const vector& x) //庫中實現模式, 直接使用類名。但C++,類型不是類名。
//這里各位讀者了解下這里直接類名做類型也正確即可,但不建議各位這樣做。
vector(const vector<T>& v)
{reserve(v.capacity());//后面會給出實現for (auto& e : v){push_back(e);}
}

2.3 賦值重載

賦值重載這里我們不要傳引用,而是直接傳參即可。編譯器調用拷貝構造生成形參后,在調用swap()函數依次交換形參和this即可。

//賦值重載
void swap(vector<T>& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v_endofstorage);
}vector<T>& operator= (vector<T> tmp)
{swap(tmp);return *this;
}		

3 析構函數

~vector()
{delete[] _start;_start = _finish = _endofstorage = nullptr;
}

三、容量相關:capacity()、size()、reserve()、resize()

3.1 capacity()

size_t capacity()const
{return _endofstorage - _start;
}

3.2 size()

size_t size()const
{return _finish - _start;
}

3.3 reserve()

由于stl中我們一般不縮容,所以先判斷reserve的空間大小是否比當前空間容量大。
如果reserve的空間更大,所以我們需要先開好目標大小的空間,在將原數據拷貝過去,最后析構原來空間即可。
但下面這兩種實現方式對嗎?

第一種:

void reserve(size_t n)
{if (n > capacity()){T* tmp = new T[n];if (_start)//如果原來空間有數據,拷貝到新空間{memcpy(tmp, _start, sizeof(T) * size());delete[] _start;}//更新_start、_finish、_endofstorage。指向新空間中相應位置_start = tmp;_finish = _start + size();_endofstorage = _start + n;}
}

先說結論:上訴這段代碼是錯的。
在我們調試后會發現_finish的值沒有更新。(這里大家自行驗證下接口)
?
原因:(win11畫圖一直很模糊,博主也很無奈,各位將就看吧)
在這里插入圖片描述


第二種:
為了解決上訴問題,我們可以先記錄_finish和_start的偏移量,用來代替size()函數。
所以初學者很容易寫出以下代碼:

void reserve(size_t n)
{if (n > capacity()){size_t sz = size();//記錄_finish 和 _start 的偏移量T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);delete[] _start;}_start = tmp;_finish = _start + sz;//不能用size()代替sz,否則會導致迭代器失效_endofstorage = _start + n;}
}

那這是否正確呢?答案是否定的。
我們來看看下面這種場景:
在這里插入圖片描述


實際上對于這種情況,可以自己循環依次賦值即可。內置類型直接拷貝數據;內置類型調用賦值重載,是一種深拷貝。

最終代碼如下:

void reserve(size_t n)
{if (n > capacity()){size_t sz = size();//記錄_finish 和 _start 的偏移量T* tmp = new T[n];if (_start){//memcpy(tmp, _start, sizeof(T) * sz);for (size_t i = 0; i < sz; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + sz;//不能用size()代替sz,否則會導致迭代器失效_endofstorage = _start + n;}
}

3.4 resize()

resize邏輯還是很簡單的。
首先判斷resize()的目標大小n和有效數據個數size()誰大。如果有效個數size()更大,只需更改_finish即可;否則要先進行擴容(reserve會將原有數據拷貝到新空間),然后從_finish開始向擴充的空間插入新的值。
?
代碼如下:

//const會延長匿名對象的生命周期, 匿名對象具有常性
//模板出來后,對類進行了升級,內置類型也有構造函數
//void resize(size_t n, T val = T())
void resize(size_t n, const T& val = T())
{if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish < _start + n){*_finish = val;_finish++;}}
}

四、operator[ ]重載

T& operator[](size_t pos)
{assert(pos < size());return _start[pos];
}const T& operator[](size_t pos)const
{assert(pos < _finish);return _start[pos];
}

五、元素相關:insert、erase、push_back、pop_back

5.1 insert()

任意位置插入數據,首先需判斷是否需要擴容。然后將插入位置pos開始往后的數據向后移動,最后將新數據插入到pos處即可。
tips:

  • 如果發生擴容,需要先記錄pos和_start之間的偏移量。在將pos位置跟新,指向新空間中對應位置。否則會導致迭代器失效
void insert(iterator pos, const T& x)
{assert(pos >= _start);assert(pos <= _finish);if (_finish == _endofstroage){size_t len = pos - _start;reserve(capacity() == 0 ? 4 : capacity() * 2);pos = _start + len;}//挪動數據iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;end--;}//插入數據*pos = x;_finish++;
}

5.2 erase()

任意位置刪除數據,只需要從pos+1開始,將后續數據全部依次向前移動覆蓋,最后更新_finish即可。

iterator erase(iterator pos)
{assert(pos >= _start);assert(pos < _finish);iterator it = pos + 1;while (it < _finish){*(it - 1) = *it;++it;}--_finish;return pos;
}

5.2.1 erase迭代器失效

void testvector4()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(4);v.push_back(5);v.push_back(6);auto it = v.begin();while (it < v.end()){if (*it % 2 == 0){v.erase(it);}it++;}for (auto e : v){cout << e << " ";}cout << endl;
}

上述代碼本意是將偶數全部刪除,結果本該是1 、5。但結果卻是:
在這里插入圖片描述
為什么呢?
這是因為我們刪除元素后,后續數據會補上空缺。所以當使用erase后,迭代器會失效。(上述結果是g++的實現機制,在vs2019下上述代碼會直接報錯。原因在于vs2019對erase后的空間做強制檢查,不允許訪問)。為此stl庫給出的解決方案是接受刪除位置的下一個元素的返回值。(這也是為什么整個模擬實現中只有erase函數具有返回值),并接收返回值。
?
正確刪除偶數方法:

void testvector4(){//std::vector<int> v;vector<int> v;v.push_back(1);v.push_back(2);v.push_back(2);v.push_back(4);v.push_back(5);v.push_back(6);auto it = v.begin();//迭代器失效/*while (it < v.end()){if (*it % 2 == 0){v.erase(it);}it++;}*/while (it < v.end()){if (*it % 2 == 0){it = v.erase(it);}else{it++;}}for (auto e : v){cout << e << " ";}cout << endl;}

5.3 push_bach()

頭插復用insert函數即可。

void push_back(const T& x)
{//if (_finish == _endofstroage)//{//	reserve(capacity() == 0 ? 4 : capacity() * 2);//}插入數據//*_finish = x;//_finish++;insert(_finish, x);
}

5.4 pop_back()

復用erase,尾刪

void pop_back()
{erase(--end());
}

六、所有代碼

vector增刪查改模擬實現gitee鏈接

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

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

相關文章

vue路由導航守衛(全局守衛、路由獨享守衛、組件內守衛)

目錄 一、什么是Vue路由導航守衛&#xff1f; 二、全局守衛 1、beforeEach 下面是一個beforeEach的示例代碼&#xff1a; 2、beforeResolve 下面是一個beforeResolve的示例代碼&#xff1a; 3、afterEach 下面是一個afterEach的示例代碼&#xff1a; 三、路由獨享守衛…

Shell - 學習筆記 - 1.14 - 如何編寫自己的Shell配置文件(配置腳本)?

第1章 Shell基礎(開胃菜) 14 - 如何編寫自己的Shell配置文件(配置腳本)? 學習了《Shell配置文件的加載》一節,讀者應該知道 Shell 在登錄和非登錄時都會加載哪些配置文件了。對于普通用戶來說,也許 ~/.bashrc 才是最重要的文件,因為不管是否登錄都會加載該文件。 我們…

【數據處理】NumPy數組的合并操作,如何將numpy數組進行合并?

&#xff0c;NumPy中的合并操作是指將兩個或多個數組合并成一個數組的操作。這種操作可以通過不同的函數來實現。 一、橫向合并&#xff08;水平合并&#xff09; 橫向合并是指將兩個具有相同行數的數組按列方向合并成一個數組的操作。在NumPy中&#xff0c;可以使用hstack()…

044:vue中引用json數據的方法

第044個 查看專欄目錄: VUE ------ element UI 專欄目標 在vue和element UI聯合技術棧的操控下&#xff0c;本專欄提供行之有效的源代碼示例和信息點介紹&#xff0c;做到靈活運用。 &#xff08;1&#xff09;提供vue2的一些基本操作&#xff1a;安裝、引用&#xff0c;模板使…

多相Buck的工作原理

什么是多相Buck電源&#xff1f; 多相電源控制器是一種通過同時控制多個電源相位的設備&#xff0c;以提供穩定的電力供應。相位是指電源中的電流和電壓波形。多相控制器的設計旨在最大程度地減小電力轉換系統的紋波&#xff0c;并提高整體能效。它通常包含一系列的功率級聯&a…

我的創作紀念日1024天紀念

機緣 經歷的1024天&#xff0c;突然有一種驚奇&#xff0c;日子一天天過&#xff0c;有種恍惚的感覺 收獲 從最開始的隨筆&#xff0c;慢慢向著筆記總結轉變&#xff0c;不經意間積累了好多 憧憬 雖不知最終會怎樣發展&#xff0c;但堅持與向前是一定的&#xff0c;未來一…

結構化布線系統

滿足下列需求&#xff1a; 1.標準化&#xff1a;國際、國家標準。 2.實用性&#xff1a;針對實際應用的需要和特點來建設系統。 3.先進性&#xff1a;采用國際最新技術。5-10年內技術不落后。 4.開放性&#xff1a;整個系統的開放性。 5.結構化、層次化&#xff1a;易于管理和維…

Matplotlib數據可視化

繪圖基礎語法 &#xff11; 創建畫布并且創建子圖 首先創建一個空白的畫布&#xff0c;并且可以將畫布分為幾個部分&#xff0c;這樣就可以在同一附圖上繪制多個圖像。 plt.figure 創建一個空白畫布&#xff0c;可以指定畫布大小、像素 figure.add_subplot 創建并且選中子…

docker鏡像、容器管理與遷移

鏡像管理 搜索鏡像&#xff1a; 這種方法只能用于官方鏡像庫 搜索基于 centos 操作系統的鏡像 # docker search centos 按星級搜索鏡像&#xff1a; 查找 star 數至少為 100 的鏡像&#xff0c;默認不加 s 選項找出所有相關 ubuntu 鏡像&#xff1a; …

【web安全】文件讀取與下載漏洞

前言 菜某整理僅供學習&#xff0c;有誤請賜教。 概念 個人理解&#xff1a;就是我們下載一個文件會傳入一個參數&#xff0c;但是我們可以修改參數&#xff0c;讓他下載其他的文件。因為是下載文件&#xff0c;所以我們可以看到文件里面的源碼&#xff0c;內容。 文件讀取…

Python嗅探和解析網絡數據包

網絡工具解釋 Scapy是Python2和Python3都支持的庫。 它用于與網絡上的數據包進行交互。 它具有多種功能&#xff0c;通過這些功能我們可以輕松偽造和操縱數據包。 通過 scapy 模塊&#xff0c;我們可以創建不同的網絡工具&#xff0c;如 ARP Spoofer、網絡掃描儀、數據包轉儲器…

swiftUi——顏色

在SwiftUI中&#xff0c;您可以使用Color結構來表示顏色。Color可以直接使用預定義的顏色&#xff0c;例如.red、.blue、.green等&#xff0c;也可以使用自定義的RGB值、十六進制顏色代碼或者系統提供的顏色。 1. 預定義顏色 Text("預定義顏色").foregroundColor(.…

Swing程序設計(9)復選框,下拉框

文章目錄 前言一、復選框二、下拉框總結 前言 該篇文章簡單介紹了Java中Swing組件里的復選框組件、列表框組件、下拉框組件&#xff0c;這些在系統中都是常用的組件。 一、復選框 復選框&#xff08;JCheckBox&#xff09;在Swing組件中的使用也非常廣泛&#xff0c;一個方形方…

Albumentations(Augmentation Transformations)

Albumentations&#xff08;Augmentation Transformations&#xff09; Albumentations&#xff08;Augmentation Transformations&#xff09;是一個用于圖像數據增強&#xff08;數據增廣&#xff09;的Python包。它提供了豐富的圖像增強技術&#xff0c;用于訓練機器學習模…

hadoop安裝與配置-shell腳本一鍵安裝配置(集群版)

文章目錄 前言一、安裝準備1. 搭建集群 二、使用shell腳本一鍵安裝1. 復制腳本2. 增加執行權限3. 分發腳本4. 執行腳本5. 加載用戶環境變量 三、啟動與停止1. 啟動/停止hadoop集群(1) 復制hadoop集群啟動腳本(2) 增加執行權限(3) 啟動hadoop集群(4) 停止hadoop集群(5) 重啟hado…

智慧社區前景無限,科技引領未來發展

社區是城鎮化發展的標志&#xff0c;作為人類現代社會的生活的基本圈子&#xff0c;是人類生活離不開的地方&#xff0c;社區人口密度大、車輛多&#xff0c;管理無序&#xff0c;社區的膨脹式發展多多少少帶來一定的管理上的缺失。社區作為智慧城市建設的重要一環&#xff0c;…

編譯基于LIO-SAM的liorf“Large velocity, reset IMU-preintegration!“

使用LIO-SAM修改的代碼liorf&#xff08;因自己使用的IMU傳感器是 6-axis ouster&#xff09;&#xff1a; LIO-SAM代碼連接&#xff1a; https://github.com/TixiaoShan/LIO-SAM liorf代碼連接&#xff1a; https://github.com/YJZLuckyBoy/liorf 編譯運行出現錯誤&#…

eve-ng山石網科HillStone鏡像部署

HillStone 部署 author&#xff1a;leadlife data&#xff1a;2023/12/4 mains&#xff1a;EVE-ng HillStone 鏡像部署 - use hillstone-sg6000 default&#xff1a;hillstone/hillstone 傳輸 scp hillstone-sg6000.zip root192.168.3.130:/opt/unetlab/addons/qemu/部署 cd …

echarts繪制一個環形圖

其他echarts&#xff1a; echarts繪制一個柱狀圖&#xff0c;柱狀折線圖 echarts繪制一個餅圖 echarts繪制一個環形圖2 效果圖&#xff1a; 代碼&#xff1a; <template><div class"wrapper"><!-- 環形圖 --><div ref"doughnutChart…

深入理解Spring Kafka中@KafkaListener注解的參數與使用方式

Apache Kafka作為一個強大的消息代理系統&#xff0c;與Spring框架的集成使得在分布式應用中處理消息變得更加簡單和靈活。Spring Kafka提供了KafkaListener注解&#xff0c;為開發者提供了一種聲明式的方式來定義消息監聽器。在本文中&#xff0c;我們將深入探討KafkaListener…