[C++]vector的模擬實現

? ? ? ? 下面是簡單的實現vector的功能,沒有涉及使用內存池等復雜算法來提高效率。

一、vector的概述

(一)、抽象數據類型定義

容器:向量(vector)
vector是表示大小可以變化的數組的序列容器。像數組一樣,向量對其元素使用連續的存儲位置,這意味著也可以使用指向其元素的常規指針上的偏移量來訪問其元素,并且與數組中的元素一樣高效。但與數組不同的是,它們的大小可以動態變化,它們的存儲由容器自動處理。

模板類

template<class T>

操作

一、構造函數
vector();? ? ? ? 默認構造
vector(size_t n,const T val = T());????????用n個val進行填充初始化

vector(int n, const T val = T());? ?防止vector(int,int)與下面的模板函數發生歧義
template<class InputIterator>????????
vector(InputIterator first, Inputiterator last);????????迭代器區間初始化

vector(std::initializer_list list);? ? ? ? 用初始化列表進行初始化
vector(const vector<T>& v);????????拷貝構造函數
vector<T>& operator=(const vector<T>& v); ? ? ? 賦值構造
~vector();????????析構函數

二、迭代器
iterator begin();
iterator end();
const_iterator begin() const;
const_iterator end() const;

三、容量
size_t size() const;????????返回當前容器存儲的數據個數
size_t capacity() const;????????返回當前容器的容量大小
void resize(size_t n);????????改變容器數據個數
bool empty() const;????????判斷當前容器是否為空
void reserve(size_t n);????????改變容量大小

四、訪問數據
T& operator[](size_t n);? ? ? ? 可讀可寫
const T& operator[](size_t n) const;? ? ? ? 只可讀

五、修改操作
void push_back(const T& val);????????尾插操作
void pop_back();????????尾刪操作
iterator insert(iterator pos, const T& x);????????在指定位置插入
iterator erase(iterator pos);? ? ? ? 指定位置刪除
void swap(vector<T>& v);????????交換兩個vector容器的數據

(二)、成員變量

template<class T>
class vector
{
public://vector的迭代器可以為原生指針typedef T* iterator;typedef const T* const_iterator;
private:iterator _start = nullptr;//指向第一個數據的迭代器iterator _finish = nullptr;//指向最后一個有效數據的后一個位置iterator _endOfStorage = nullptr;//指向數組末尾的后一個位置
};

? ? ? ? ?這里的迭代器就是我們的原生指針T*,它的私有成員變量就和線性表一樣,要存儲數據的起始位置的指針、數據的有效數據的個數、存儲容量等。這里直接全部定義成指針來維護容器的數據數組,_start指向數組的起始位置,_finish指向有效數據個數的后一個的位置,當我們將這兩個指針相減,_finish - _start?就能得到數據的有效數據個數了。最后一個endOfStorage就是指向數組的末尾的下一個位置(雖然越界但不解引用就沒事),當我們拿_endOfStorage -??_start 就得到了這段空間的容量個數。

? ? ? ? 注意:我們的每個構造函數,在一開始時都要把迭代器置為空指針,而我們在聲明迭代器時,順便給上初始值,這樣會更好,以免忘記初始化帶來不必要的麻煩。

迭代器維護的空間示意圖:

二、具體實現

? ? ? ? 我們不按上面的函數聲明的順序來依次定義函數,我們按照從易到難的順序,一點一點實現其成員函數的定義,每個階段測試一下。

(一)、默認構造、析構、迭代器、尾插、尾刪、下標訪問

(1)、默認構造、析構

//默認構造
vector() :_start(nullptr), _finish(nullptr), _endOfStorage(nullptr) { ; }

? ? ? ? ?因為是空容器,所以三個指針都初始化為nullptr。

//析構
~vector()
{delete[] _start;_start = _finish = _endOfStorage = nullptr;
}

? ? ? ? 刪除動態分配的空間后記得將指針置為空,因為析構是可以手動調用的。

(2)、迭代器

//迭代器
iterator begin() { return _start; }
iterator end() { return _finish; }
const_iterator begin() const { return _start; }
const_iterator end() const { return _finish; }

? ? ? ?兩組迭代器分別構成重載。

????????注意:重載不是因為返回值的不同,而是其參數不同,一個是this,另一個是const this的指針。

(3)、尾插和尾刪操作

? ? ? ? ?注意:需要對容器滿時進行擴容處理。

1、輔助其擴容的函數

empty():判斷容器是否為空

bool empty() const { return (_finish - _start) == 0; }

size():返回容器的有效數據個數

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

capacity():返回容器的容量

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

reserve():擴容操作

void reserve(size_t n)//改變容量大小
{//如果n為0,默認給4個空間n = n == 0 ? 4 : n;//不允許縮容if (n <= capacity())return;//擴容size_t nSize = size();//保存有效數據個數iterator itTem = new T[n];//拷貝到新數組for (int i = 0; i < nSize; i++){*(itTem + i) = *(_start + i);}delete[] _start;_start = itTem;_finish = _start + nSize;_endOfStorage = _start + n;
}
2、尾插、尾刪
//修改操作
void push_back(const T& val)//尾插操作
{//判斷容器空間是否足夠if (size() == capacity()){size_t newCapacity = capacity() * 2;//兩倍擴容//擴容reserve(newCapacity);//std::cout << capacity() << " ";}//尾插*_finish = val;_finish++;
}
void pop_back()//尾刪操作
{if (!empty())_finish--;
}

(4)、下標訪問

		//訪問操作T& operator[](size_t n){//防止越界訪問assert(n >= 0 && n < size());return *(_start + n);}const T& operator[](size_t n) const{//防止越界訪問assert(n >= 0 && n < size());return *(_start + n);}

? ? ? ? 注意:構成函數重載的是參數,不是返回值。

(5)、代碼測試

void test1()
{const int N = 10;//測試數據的個數MySpace::vector<int> v1;for (int i = 0; i < N; i++){v1.push_back(i + 1);}//手動使用迭代器遍歷MySpace::vector<int>::iterator it1 = v1.begin();while (it1 != v1.end()){cout << *it1 << " ";it1++;}cout << endl;//數組方式訪問for (int i = 0; i < N; i++)cout << v1[i] << " ";cout << endl;//測試尾刪for (int i = 0; i < N + 2; i++)//+2是為了測試一下刪空容器{cout << endl;for (auto e : v1)//測試范圍for{cout << e << " ";}v1.pop_back();}
}

運行結果:

1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10


1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9
1 2 3 4 5 6 7 8
1 2 3 4 5 6 7
1 2 3 4 5 6
1 2 3 4 5
1 2 3 4
1 2 3
1 2
1



(二)、其余的構造函數

(1)、迭代器區間和參數初始化表初始化

迭代器區間初始化構造函數

????????因為迭代器的類型可能不同,不一定所有的迭代器的類型都是指針,有可能是自定義類型。所以用模板函數。

template<class InputIterator>
vector(InputIterator first, InputIterator last)
{size_t size = last - first;reserve(size);//提前開好空間,防止頻繁擴容//一個一個數據尾插while (first != last){push_back(*first);first++;}
}

參數初始化列表初始化構造函數

//復用上面的迭代器區間構造函數即可
vector(std::initializer_list<T> list) :vector(list.begin(), list.end()) { ; }

(2)拷貝構造函數

		//拷貝構造vector(const vector<T>& v){reserve(v.capacity());for (auto& e : v){push_back(e);}}

(3)交換兩個容器和賦值構造函數

實現交換容器的函數swap()來輔助賦值構造函數的實現

void swap(vector<T>& v)//交換兩個的數據
{std::swap(_start, v._start);std::swap(_finish,v._finish);std::swap(_endOfStorage, v._endOfStorage);
}

賦值構造函數

//賦值構造
vector<T>& operator=(const vector<T>& v)
{//復用拷貝構造函數vector<T> tem(v);swap(tem);return *this;
}

(4)填充初始化

		//填充初始化vector(size_t n, const T val = T())//用n個val進行初始化{reserve(n);for (int i = 0; i < n; i++){push_back(val);}}//用來防止調用vector(int,int)時發生歧義//發生歧義的函數:vector<InputIterator InputIterator>vector(int n, const T val = T()){reserve(n);for (int i = 0; i < n; i++){push_back(val);}}

(5)、代碼測試

void test2()
{const int N = 8;MySpace::vector<int> v1;for (int i = 0; i < N; i++){v1.push_back(i + 1);}for (auto e : v1){cout << e << " ";}cout << endl;//測試迭代器區間初始化構造MySpace::vector<int> v2(v1.begin() + 2, v1.end()-2);for (auto e : v2){cout << e << " ";}//測試參數初始化列表構造cout << endl;MySpace::vector<int> v3({ 1,2,3,4,5,6,7,8,9,10 });for (auto e : v3){cout << e << " ";}//測試拷貝構造cout << endl;MySpace::vector<int> v4(v3);for (auto e : v4){cout << e << " ";}//測試賦值構造cout << endl;MySpace::vector<int> v5;v5 = v4;for (auto e : v5){cout << e << " ";}//測試填充初始化cout << endl;MySpace::vector<int> v6(10,7);for (auto e : v6){cout << e << " ";}
}

運行結果:?

1 2 3 4 5 6 7 8
3 4 5 6
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9 10
7 7 7 7 7 7 7 7 7 7?

(三)、指定位置插入和刪除

(1)、指定位置插入

		iterator insert(iterator pos, const T& x)//在指定位置插入{//判斷插入的位置及是否合法assert(pos >= _start && pos <= _finish);//判斷是否需要擴容if (size() == capacity()){//防止擴容后pos迭代器失效size_t nPos = pos - _start;//計算pos距離_start的偏移量size_t newCapacity = capacity() * 2;reserve(newCapacity);pos = _start + nPos;}//將插入位置及之后的元素往后挪iterator it = _finish;while (it > pos){*it = *(it - 1);it--;}//插入元素*pos = x;_finish++;return pos;}

????????注意:擴容時pos迭代器會失效,擴容前要計算pos距離起始位置的偏移量,然后再更新pos的迭代器。所以,外部的迭代器pos是有失效的可能性的,而不管怎么樣外部的pos是不能再繼續使用的,若要繼續使用,則需要接受函數的返回值更新pos的迭代器,防止迭代器失效,此時pos迭代器指向新插入的數據。

(2)、指定位置刪除?

iterator erase(iterator pos)//指定位置刪除
{//判斷刪除的位置是否合法assert(pos >= _start && pos <= _finish - 1);//將pos位置后的元素往前挪iterator cur = pos + 1;while (cur != _finish){*(cur - 1) = *cur;cur++;}_finish--;return pos;
}

? ? ? ? 注意:vector刪除元素時有可能縮容的,雖然這里實現的vector不會縮容,為了防止外部的迭代器失效,我們也需要返回一個迭代器更新其pos的位置,此時pos指向的位置是刪除的元素的下一個元素的位置。

(3)、代碼測試

void test3()
{MySpace::vector<int> v1{ 1,2,3,4,5,6 };//測試指定位置插入v1.insert(v1.begin(), 999);v1.insert(v1.end(), 999);v1.insert(v1.end() - 2, 999);for (auto e : v1){cout << e << " ";}cout << endl;//結合find刪除所有999//找不到則返回結尾的迭代器MySpace::vector<int>::iterator itFind;while ((itFind = std::find(v1.begin(), v1.end(), 999)) != v1.end()){cout << endl;v1.erase(itFind);for (auto e : v1){cout << e << " ";}}
}

(4) 運行結果:

999 1 2 3 4 5 999 6 999?


1 2 3 4 5 999 6 999
1 2 3 4 5 6 999
1 2 3 4 5 6

(四)、修改有效數據個數大小

具體實現代碼:

void resize(size_t n)//改變容器數據個數
{assert(n >= 0);//判斷是否需要擴容if (n > capacity()){reserve(n);}//將其他位置初始化為數據的默認構造for (int i = size(); i < n; i++){*(_start + i) = T();}_finish = _start + n;
}

代碼測試:

void test4()
{//測試resizeMySpace::vector<int> v1{ 1,2,3,4,5,6,7,8,9,10 };for (auto e : v1){cout << e << " ";}cout << endl;//縮容v1.resize(2);for (auto e : v1){cout << e << " ";}cout << endl;//調大有效數據個數大小v1.resize(10);for (auto e : v1){cout << e << " ";}
}

1 2 3 4 5 6 7 8 9 10
1 2
1 2 0 0 0 0 0 0 0 0?

? ? ? ? 以上就是vector的簡易實現。

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

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

相關文章

帶你學習Mybatis之Mybatis映射文件

Mybatis映射文件 增刪改查 簡單地增刪改查 <select id"selectUser" resultType"User"> select * from user where id #{id}</select><insert id"addUser"> insert into user (name,account) values (#{name},#{account…

[sylar]后端學習:配置環境(一)

1.介紹 基于sylar大神的C高性能后端網絡框架來進行環境配置和后續學習。網站鏈接&#xff1a;sylar的Linux環境配置 2.下載 按照視頻進行下載&#xff0c;并進行下載&#xff0c;并最好還要下載一個vssh的軟件。可以直接在網上搜索即可。 sylar_環境配置&#xff0c;vssh下…

CentOS 運維常用的shell腳本

文章目錄 一、操作系統磁盤空間查看實時獲取系統運行狀態獲取cpu、內存等系統運行狀態獲取系統信息二、應用程序獲取進程運行狀態查看有多少遠程的 IP 在連接本機三、用戶管理統計當前 Linux 系統中可以登錄計算機的賬戶有多少個創建用戶四、自動化管理自動備份日志文件監控的頁…

MySQL常見操作

MySQL字符串連接 在MySQL中&#xff0c;字符串連接可以使用CONCAT()函數或雙豎線||操作符進行。下面是兩種方法的示例&#xff1a; 使用CONCAT()函數&#xff1a; CONCAT(,2001,, ABC)使用雙豎線||操作符&#xff1a; ,2001, || ABC您可以根據自己的偏好選擇其中一種方法來…

TS38.300中的切換流程(很一般)

本文根據3GPP R18 TS 38.300第9.2.3節整理 切換(Handover)是移動終端(UE)進入RRC_CONNECTED狀態后在不同服務小區(Cell)之間保持與網絡聯系唯一手段&#xff0c;期間首先通過控制面(C-Plane)進行無線測量、切換協商及觸發等&#xff1b;為此3GPP在TS38.300中定義如下。 RAN系統…

shardingsphere5 自定義分片(sharding-algorithm)算法

背景 在做分表時&#xff0c;需要自定義算法。 這里實現的算法是&#xff1a; 分表字段的 hashCode 取余。 算法 public class UserShardingAlgorithm implements StandardShardingAlgorithm<String> {public static String type "USER_SHARDING_STRATEGY"…

2024KCon大會議題招募火熱進行中

歷時1個多月我們收到了來自全國各地小伙伴們的議題投遞既有前瞻性的技術研判亦有安全領域的最新策略......感謝每一位對KCon大會傾注熱情與支持的你&#xff01; 我們也收到了不少小伙伴的私信&#xff0c;有的因為工作繁忙有的因為在緊張備戰2024網絡安全攻防演練表示原定的時…

LeetCode2542最大子序列的分數

題目描述 給你兩個下標從 0 開始的整數數組 nums1 和 nums2 &#xff0c;兩者長度都是 n &#xff0c;再給你一個正整數 k 。你必須從 nums1 中選一個長度為 k 的 子序列 對應的下標。 對于選擇的下標 i0 &#xff0c;i1 &#xff0c;…&#xff0c; ik - 1 &#xff0c;你的 …

監控易監測對象及指標之:全面監控LDAP服務器

隨著企業信息化建設的不斷深入&#xff0c;LDAP&#xff08;輕量級目錄訪問協議&#xff09;服務器作為重要的目錄服務組件&#xff0c;其穩定性和性能直接關系到企業業務的連續性和 效率。為了確保LDAP服務器的穩定運行和高效性能&#xff0c;對其進行全面監控顯得尤為重要。…

Kafka原生API使用Java代碼-消費者組-消費模式

文章目錄 1、消費模式1.1、創建一個3分區1副本的 主題 my_topic11.2、創建生產者 KafkaProducer11.2、創建消費者1.2.1、創建消費者 KafkaConsumer1Group1 并指定組 my_group11.2.3、創建消費者 KafkaConsumer2Group1 并指定組 my_group11.2.3、創建消費者 KafkaConsumer3Group…

算法練習第25天|491. 非遞減子序列

491. 非遞減子序列 491. 非遞減子序列https://leetcode.cn/problems/non-decreasing-subsequences/ 題目描述&#xff1a; 給你一個整數數組 nums &#xff0c;找出并返回所有該數組中不同的遞增子序列&#xff0c;遞增子序列中 至少有兩個元素 。你可以按 任意順序 返回答案…

Flutter 中的 ButtonTheme 小部件:全面指南

Flutter 中的 ButtonTheme 小部件&#xff1a;全面指南 Flutter 是一個由 Google 開發的跨平臺 UI 框架&#xff0c;它提供了一系列的組件來幫助開發者構建美觀且功能豐富的應用。在 Flutter 的組件庫中&#xff0c;ButtonTheme 是一個重要的小部件&#xff0c;它允許開發者統…

Linux、Windows安裝python環境(最新版及歷史版本指定版本)-python

目錄 一、Linux環境二、windows環境最新版本下載指定版本下載 python 官網地址&#xff1a; https://www.python.org/ 一、Linux環境 以openEuler/CentOS為例 查看可安裝python源版本 dnf provides python*默認安裝新版本 dnf install -y python3. 進入python python退出p…

電源小白入門學習8——電荷泵電路原理及使用注意事項

電源小白入門學習8——電荷泵電路原理及使用注意事項 電荷泵簡介電荷泵原理電荷泵設計過程中需要注意的點fly電容的安秒平衡DC/DC功率轉換技術對比 電荷泵簡介 電荷泵&#xff08;Charge Pump&#xff09;是一種電路拓撲結構&#xff0c;用于實現電壓升壓或降壓的功能。它通過…

Python自動化測試斷言詳細實戰代碼(建議收藏)

&#x1f345; 視頻學習&#xff1a;文末有免費的配套視頻可觀看 &#x1f345; 點擊文末小卡片 &#xff0c;免費獲取軟件測試全套資料&#xff0c;資料在手&#xff0c;漲薪更快 在測試用例中&#xff0c;執行完測試用例后&#xff0c;最后一步是判斷測試結果是 pass 還是 fa…

sh發送郵件如何通過配置SMTP服務器來實現?

sh發送郵件的操作方法&#xff1f;如何使用Shell腳本自動發信&#xff1f; 在Shell腳本中實現郵件發送功能是一項常見需求&#xff0c;特別是在自動化任務執行或系統監控中。AokSend將介紹如何通過配置SMTP服務器來實現sh發送郵件的方法和注意事項。 sh發送郵件&#xff1a;安…

Redash、Superset、DataEase、Metabase、FineBI 和 Power BI 報表系統的優缺點

最近在做報表系統的選型與調研&#xff0c;其中嘗試了Redash、Superset、DataEase、Metabase、FineBI 和 Power BI幾個報表系統&#xff0c;主要想使用開源免費的&#xff0c;如果大家有好用的報表系統推薦歡迎留言。 Redash 優點&#xff1a; 開源且免費&#xff1a;Redash…

【已解決】Error in the HTTP2 framing layer

1.問題描述 在使用git將代碼上傳github的時候在最后一部push的時候遇到這個fatal 2.解決方案 由于我原先設置的origin是http協議下的&#xff0c;如下 git remote add origin https://github.com/Charlesbibi/Simple_Cloud.githttp協議下行不通不妨試一試ssh協議下&#xff…

跟風報考PMP,我真的后悔了

真的太香吧&#xff01; 我一開始沒打算報考PMP證書的&#xff0c;但是我看身邊很多朋友都因為PMP證書得到了升職加薪&#xff0c;這讓我實在是一整個羨慕住了&#xff0c;所以我也去報考了PMP。 報考PMP前期我做了什么&#xff1f; 由于我是零基礎&#xff0c;沒有什么項目…

探索網格生成技術在AI去衣應用中的作用

引言&#xff1a; 隨著人工智能技術的飛速發展&#xff0c;其在圖像處理和計算機視覺領域的應用日益廣泛。其中&#xff0c;AI去衣技術作為一種新興的應用&#xff0c;引起了廣泛的關注和討論。然而&#xff0c;要實現這一功能并非易事&#xff0c;需要借助于先進的算法和技術。…