【C++】vector的底層原理講解及其實現

目錄

一、認識vector底層結構
二、初始化vector的函數

  1. 構造函數
  2. 拷貝構造
  3. 賦值構造
  4. initializer_list構造
  5. 迭代器區間構造

三、迭代器
四、數據的訪問
五、容量相關的函數
六、關于數據的增刪查改操作


一、認識vector底層結構
STL庫中實現vector其實是用三個指針來完成的,使用這三個指針(或稱為迭代器)變量來管理其內存

在這里插入圖片描述
其實vector和string的實現非常相似,都是利用了順序表結構,在vector的實現上我們遵循底層用三個指針來完成,_statr,_finish,_end_fo_storage分別指向順序表的頭,順序表存儲數據的有效個數的位置,順序表的結束

template<class T>
class vector
{
public:typedef T* iterator;typedef const T* const_iterator;private:iterator _start;iterator _finish;iterator _endofstorage;
};

二、初始化vector的函數

1、構造函數
①最常用的無參默認構造
vector();

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

這個很簡單,我就不多講了,后面遇到難點我會詳細說明,便于大家理解

②用n個val構造一個vector
explicit vector (size_type n, const value_type& val = value_type();
庫里面用explicit修飾構造函數,是為了防止構造函數發生隱式類型轉換

vector(size_t n, const T& val = T())
{_start = new T[n];_finish = _start;while (_finish!=_start+n){*_finish = val;_finish++;}_endofstorage = _start + n;
}

2、拷貝構造
vector(const vector& v);
拷貝構造:用一個已經存在的對象來初始化另一個正在創建的對象

vector(const vector& v)
{_start = new T[v.size()];memcpy(_start, v._start, sizeof(T) * v.size());_finish = _start + v.size();_endofstorage = _finish;
}

3、賦值構造
賦值構造:兩個已經存在的對象,一個賦值給另一個
vector& operator= (const vector& v);

void swap(vector& v)
{std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);
}
//v1=v2;
vector& operator= (vector v)
{swap(v);return *this;
}

這里我利用庫里的函數來實現swap,只交換vector的三個指針更高效,賦值構造的參數是傳值傳參,會調用拷貝構造,將v2拷貝一份給v,然后之間調用swap函數,將v和this交換,最后返回this即可

4、initializer_list構造
vector (initializer_list<T> il);
tips:這里的initializer_list實際是個類,C++底層將其封裝了,里面也有begin,end,size

//vector<int> v={1,2,3,4,5};
vector(initializer_list<T> il)
{for (auto e : il){push_back(e);}
}

5、迭代器區間構造
template <class InputIterator> vector(InputIterator first, InputIterator last);

// 類模板的成員函數可以是函數模板
template <class InputIterator>
vector(InputIterator first, InputIterator last)
{while (first != last){push_back(*first);++first;}
}

注意:如果加了迭代器區間構造會造成一個問題,就是在調用時和vector(size_t n, const T& val = T())會出現沖突,底層給出的解決方案就是加一個重載vector(int n, const T& val = T())

三、迭代器
這里博主就只介紹最重要的迭代器部分
template<class T>
class vector{public:typedef T* iterator;typedef const T* const_iterator;iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const//加了const修飾,const對象可以調用,非const對象也可以調用{return _start;}const_iterator end() const{return _finish;}private:iterator _start; //指向空間(順序表)的開始iterator _finish;//指向有效數據個數的位置iterator _endofstorage;//指向空間的結束
};

四、數據的訪問

由于vector底層是連續的物理空間,所以支持下標訪問
T& operator[] (size_t n);
const T& operator[] (size_t n) const;

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

五、容量相關的函數

size(有效數據個數)和capacity(空間容量大小)

size_t size() const
{return _finish - _start;//指針減指針得到兩個指針之間的數據個數
}
size_t capacity() const
{return _endofstorage - _start;
}

reserve
void reserve (size_t n);
易錯點1
在這里插入圖片描述

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* temp = new T[n];memcpy(temp, _start, sizeof(T) * size());delete[]_start;_start = temp;_finish = _start + old_size;_endofstorage = _start+n;}
}

改成現在這個樣子確實是解決了上圖中的問題,但是這個就是對的嗎?讓我們一起來看一下這個代碼究竟對不對

易錯點2
其實上面的代碼大體邏輯是沒有什么問題的,問題就出在這個memcpy上,要知道memcpy底層實現是按字節一個一個拷貝過去的,雖然我們的vector是深拷貝,但是memcpy是淺拷貝,這樣寫針對內置類型是🆗的,但是針對自定義類型會存在指向同一塊空間的問題,兩次析構等問題,話不多說,我們看圖解

出錯案例:
在這里插入圖片描述
出錯圖解:
在這里插入圖片描述
其實要改正這個問題有一個很簡單的方式,采用賦值的方式,無論是內置類型還是自定義類型,在賦值時都會調用他的拷貝構造,這樣就自動調用該類型的深拷貝了

void reserve(size_t n)
{if (n > capacity()){size_t old_size = size();T* temp = new T[n];//memcpy(temp, _start, sizeof(T) * size());for (size_t i = 0; i < old_size; i++){temp[i] = _start[i];}delete[]_start;_start = temp;_finish = _start + old_size;_endofstorage = _start+n;}
}

resize
void resize (size_t n, const T& val=T());

void resize(size_t n, const T& val=T())
{if (n > capacity()){reserve(n);for (size_t i = size(); i < n; i++){_start[i] = val;}_finish = _start + n;}else{_finish = _start + n;}
}

注意:reverseresize都不會縮容

empty
bool empty() const

bool empty()const
{return _finsh == _start;
}

六、關于數據的增刪查改操作

push_back
void push_back (const T& val);

void push_back(const T& val)
{if (size() == capacity()){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = val;_finish++;
}

inserrt
void insert (iterator pos, const T& val);

void insert(iterator pos, const T& val)
{assert(pos>=_start);assert(pos <= _finish);size_t d = pos - _start;//先記下pos和_start的相對位置if (size() == capacity()){reserve(capacity() == 0 ? 4 : 2 * capacity());//如果擴容了,要更新pospos = _start + d;}iterator end = _finish;while (pos < end){*end = *(end - 1);end--;}*pos = val;_finish++;
}

erase
iterator erase (iterator pos);

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

clear
void clear();

void clear()
{_finish = _start;
}

vector章節我們就到這里啦,歡迎大家來學習指教下一篇list章節😘

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

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

相關文章

Promise 還能這樣理解呀!

目錄&#xff1a; 1、Promise是什么 2、Promise三種狀態 3、Promise如何使用 4、Promise作用

一種快速提升文件傳輸速度的方法

在面對網絡條件不理想時&#xff0c;進行文件傳輸往往會導致傳輸速率的顯著下降。為了克服這一難題&#xff0c;鐳速軟件特別引入了一系列創新的設置選項&#xff0c;旨在顯著提升文件傳輸速率。通過這些優化措施&#xff0c;用戶即使在網絡不佳的情況下&#xff0c;也能享受到…

機器人工具箱學習(三)

一、動力學方程 機器人的動力學公式描述如下&#xff1a; 式中&#xff0c; τ \boldsymbol{\tau} τ表示關節驅動力矩矢量&#xff1b; q , q ˙ , q \boldsymbol{q} ,\; \dot{\boldsymbol { q }} ,\; \ddot{\boldsymbol { q }} q,q˙?,q?分別為廣義的關節位置、速度和加速…

uniapp如何打包預約上門按摩APP

uniapp如何打包預約上門按摩APP&#xff1f; 開發工具&#xff1a;HBuilderX 一、創建移動應用 1、 點擊此處微信開放平臺 2、點擊【管理中心 - 移動應用 - 創建移動應用】填寫資料后等待審核 app運行流程圖 簽名如何獲取&#xff1a; 1&#xff09;先把打包好的app安裝在手…

uniapp 小程序低功耗藍牙配網 ble配網 物聯網

1.獲取藍牙列表 bleList.vue <template><view><button touchstart"startSearch">獲取藍牙列表</button><scroll-view :scroll-top"scrollTop" scroll-y class"content-pop"><viewclass"bluetoothItem&q…

java多線程——線程池

概述 線程池是管理java線程生命周期的工具 降低資源消耗。通過池化技術能夠重復利用已創建的線程&#xff0c;降低線程頻繁創建和銷毀造成的資源消耗提高線程的可管理性。無需程序員手動銷毀線程&#xff0c;控制線程創建的數量&#xff0c;避免無限制的創建影響系統穩定性 …

找不到kotlin.Pair的類文件

需要添加kotlin的依賴&#xff1a; implementation "org.jetbrains.kotlin:kotlin-stdlib:1.8.22"

OpenHarmony上移植memtester

1. 下載源碼&#xff1a; wget https://pyropus.ca./software/memtester/old-versions/memtester-4.6.0.tar.gz 2. 解壓并指定交叉編譯方式 解壓 tar -xvf memtester-4.6.0.tar.gz 修改conf-cc和conf-ld&#xff0c;指定交叉編譯方式 conf-cc conf-ld 3. 編譯 直接運行m…

Ubuntu安裝ZLMediaKit

方式一 1、安裝vcpkg 在Ubuntu上安裝vcpkg的步驟如下&#xff1a; 安裝必要的依賴&#xff1a; 首先&#xff0c;你可能需要安裝cmake和ninja-build。你可以使用apt包管理器來安裝它們&#xff1a; bash復制代碼sudo apt install cmake ninja-build下載vcpkg源碼&#xff1a;…

后端開發面經系列 -- 阿里C++二面面經

阿里C二面面經 公眾號&#xff1a;阿Q技術站 來源&#xff1a;https://www.nowcoder.com/feed/main/detail/fc4a48403b534aafa6a6bce14b542c4e?sourceSSRsearch 1、智能指針&#xff1f; std::shared_ptr&#xff1a; 原理&#xff1a;std::shared_ptr是基于引用計數的智能指…

Stable Diffusion入門使用技巧及個人實例分享--大模型及lora篇

大家好&#xff0c;近期使用Stable Diffusion比較多&#xff0c;積累整理了一些內容&#xff0c;得空分享給大家。如果你近期正好在關注AI繪畫領域&#xff0c;可以看看哦。 本文比較適合已經解決了安裝問題&#xff0c;&#xff08;沒有安裝的在文末領取&#xff09; 在尋找合…

【RAG】Linux系統下ppt轉pptx,讀取解析pptx文本數據

前情提要 檢索增強生成&#xff08;RAG&#xff09;技術&#xff0c;作為 AI 領域的尖端技術&#xff0c;能夠提供可靠且最新的外部知識&#xff0c;極大地便利了各種任務。在 AI 內容生成的浪潮中&#xff0c;RAG 通過其強大的檢索能力為生成式 AI 提供了額外的知識&#xff…

vue3 動態加載頁面

首先&#xff0c;通過下面代碼告訴編譯器要編譯哪些頁面 static modules import.meta.glob(./views/**/*.vue);然后動態加載函數這樣寫&#xff1a; static asyncLoadView (path: string) > {return defineAsyncComponent({loader: <any>Global.modules[./views/${…

Redis的跳表:高效實現有序集合

在 Redis 中&#xff0c;跳表&#xff08;Skip List&#xff09;是一種常用的數據結構&#xff0c;用于實現有序集合&#xff08;Sorted Set&#xff09;。跳表是一種基于鏈表的數據結構&#xff0c;具有快速的查找、插入和刪除操作&#xff0c;適用于有序集合的實現。 本文將…

分布式搜索——ElasticSeach簡介

一般都用數據庫存儲數據&#xff0c;然后對數據庫進行查詢獲取數據&#xff0c;但是當數據量很大時&#xff0c;查詢效率就會很慢&#xff08;具體下面會講到&#xff09;&#xff0c;所以這種情況下就會使用到ElasticSeach ElasticSeach的基本介紹 ElasticSeach是一 款非常強…

2024重慶高等教育博覽會|2024重慶高教展|全國高等教育博覽會

2024重慶高等教育博覽會|2024重慶高教展|全國高等教育博覽會 第62屆全國高等教育博覽會&#xff08;2024.秋季重慶&#xff09; 時間&#xff1a;2024年11月15-17日 地點&#xff1a;重慶國際博覽中心 組織機構 主辦單位&#xff1a;中國高等教育學會 承辦單位&#xff1a;國藥…

杰發科技AC7801——ADC之Bandgap和內部溫度計算

0. 參考 電流模架構Bandgap設計與仿真 bandgap的理解&#xff08;內部帶隙電壓基準&#xff09; ? ? 雖然看不懂這些公式&#xff0c;但是比較重要的一句應該是這個&#xff1a;因為傳統帶隙基準的輸出值為1.2V ? 1. 使用 參考示例代碼。 40002000是falsh控制器寄…

NXP RT1176(一)——二級BootLoader開發(安全引導加載程序SBL)

目錄 1. 開發環境 2. 二級BOOT的功能 3. 步驟 3.1 配置源碼 3.2 構建項目 3.2.1 MDK 3.2.2 IAR&#xff08;IAR也編譯一下工程看看&#xff0c;這樣兩個平臺都可以支持了&#xff09; 單核M7的開發&#xff01;&#xff01; 1. 開發環境 本文Windows下開發&#xff1a;…

【無標題】vo dto

在Java中&#xff0c;VO、PO、DTO都是常用的數據對象模型。 VO&#xff08;Value Object&#xff09;是值對象&#xff0c;通常用于表示一個業務實體或者頁面展示的內容。VO通常包含了多個屬性&#xff0c;并且這些屬性的類型和名稱與業務相關。VO并不一定與數據庫中的表結構相…

MHD、MQA、GQA注意力機制詳解

MHD、MQA、GQA注意力機制詳解 注意力機制詳解及代碼前言&#xff1a;MHAMQAGQA 注意力機制詳解及代碼 前言&#xff1a; 自回歸解碼器推理是 Transformer 模型的 一個嚴重瓶頸&#xff0c;因為在每個解碼步驟中加 載解碼器權重以及所有注意鍵和值會產生 內存帶寬開銷 下圖為三…