手撕STL——vector

目錄

引言

1,了解 STL 中的 vector?

2,先來一個簡易版跑起來

2_1,構造函數

2_2,擴容reserve()

2_3,push_back()

2_4,pop_back()

2_5,測試一下

3,迭代器

3_1,如何設計iterator

3_2,begin(),end()一鍋端

4,功能完善

4_1,operator[ ]()

4_2,size() 和 capacity()

4_3,resize(size_t n, T? x = T())

4_4,insert()

4_5,erase()

4_6,構造函數重載——迭代器

4_7,拷貝構造函數

4_8,賦值運算符重載

4_9,析構函數

5,總結

革命尚未成功,同志仍須努力


引言

上一期我們手撕了list,這一期我們就來手撕一個vector(代碼在最后),vector手撕起來就比list簡單很多啦。

1,了解 STL 中的 vector?

在學習初階數據結構的時候,順序表咱們也撕過好多次了,有靜態的,也有動態的,在STL中,vector是一個動態開辟的順序表,但是它與咱們在數據結構階段實現的順序表是有差異的,在數據結構階段實現的順序表中,我們是通過 typename* a, int size, int capacity,作為成員變量

#define typename intstruct node
{typename* a;int size;int capacity;
}

但是哈,在vector中,就變得不一樣啦。

template <typename T>
class vector
{private:T* _start;T* _finish;T* _end_of_storage;
}

STL 中的 vector,用了三個指針當成員變量,為什么要這樣設計呢?

在C中,咱們用的是,struct,struct的成員變量咱們是可以直接訪問的,但是在C++的class中,為了保護成員變量,咱們將其設置成private成員,咱們在外面是不能直接訪問的,vector中有size()和capacity()函數,來獲取size和capacity。但是其他的思路大致是不變的,那咱們就開始手撕吧。

2,先來一個簡易版跑起來

2_1,構造函數

2_2,擴容reserve()

vector中數據是存在一段連續的空間,不能像list那樣插入前new一個空間出來,我們必須提前開好空間,保證插入數據的時候空間一個是足夠的。

reserve的實現過程很簡單,在數據結構初階階段我們用realloc進行擴容,其大致原理就是,如果這段連續的空間后面的空間夠就直接原地擴容,如果不夠就找一個空間夠的地方,重新開辟空間,并將原有的數據拷貝過去,最后將原有的空間釋放。

我們直接用new和delete進行異地擴容,開辟好空間后再進行數據拷貝,拷貝完數據記得釋放原來的空間,避免內存泄漏。

2_3,push_back()

因為順序表頭插需要挪動數據,代價是比較大的,STL中的vector也沒有提供頭插接口,所以這里只來一個頭插

尾插分3步

1,檢查需不需要擴容

2,將 _finish 位置賦值

3,++finish

2_4,pop_back()

尾刪就比較簡單啦,直接 --_finish,就可以了

2_5,測試一下

咱們目前還沒有完成其他的函數,為了測試,咱們寫一個打印函數print()

ok,沒有問題,是可以跑起來了的。

3,迭代器

3_1,如何設計iterator

迭代器在STL中是一個非常重要的設計,它觀察STL,當然在vector中的迭代器是比較簡單的。

因為vector中的數據是存儲在一段連續的空間中,咱們的iterator就可以直接由指針typedef得來。

typedef T* iterator;
typedef const T* const_iterator;

3_2,begin(),end()一鍋端

4,功能完善

4_1,operator[ ]()

中括號重載是一個比較重要的函數,有他的存在我們就能很方便的插入修改數據。

4_2,size() 和 capacity()

直接用指針相減的中間元素個數的知識實現

4_3,resize(size_t n, T? x = T())

resize()的功能大家一定要清楚,他的注意功能是改變size。

如果 n? <?size()?就直接刪除數據,通過改變 _finish? 實現

如果 size()?<? n < capacity (),改變size,并進行初始化

如果? capacity () <? n,先開空間,在改變size,并進行初始化

4_4,insert()

insert 大致分三步

1,檢查是否需要擴容

2,挪動數據

3,插入

這里要注意一個問題,當我們進行擴容后,原來的迭代器所指向的位置已經被delete清理了,這時候的iterator實際上是一個野指針。

為了防止出錯,我們要在最開始通過指針的減法記錄迭代器的位置,后面通過加法還原迭代器。

4_5,erase()

這里也用注意迭代器失效的問題

雖然我們這里沒有擴容,進行刪除后,迭代器仍然指向原來的位置

但是還是會有一點點風險,為了確保萬無一失,咱們這里還是進行了存儲迭代器位置,還原迭代器的操作。

4_6,構造函數重載——迭代器

在STL中,我們常可以看見用迭代器去構造一個容器,vector當然也可以,咱們就來實現一下

其實也是比較簡單啊,通過迭代器,我們可以變量它,得到它的數據,再通過push_back()插入,遍歷完了之后,vector也構造完了。為了可以使所有的迭代器都可以對其進行傳參構造,這里還需要用到模板。

4_7,拷貝構造函數

根據參數直接構造。

先根據 被拷貝的對象 的 capacity 對要拷貝的對象進行擴容,再進行遍歷和賦值。

4_8,賦值運算符重載

還是有傳統寫法與現代寫法。

傳統寫法是 遍歷參數的數據?給需要被賦值的對象進行數據的賦值

咱們這里寫現代寫法。

傳參的時候不傳引用,編譯器根據咱們的拷貝構造函數進行拷貝,使用swap函數進行交換,對于參數 x 而言,它的數據存儲的空間再堆上,但是x的三個成員變量會在賦值運算符重載這個函數結束的時候進行空間的回收,但是在堆的那一部分數據還是存在的。

使用 swap 函數進行交互 只是交換了 參數 x 和 待賦值的對象的三個指針,讓待賦值的對象的三個成員遍歷指向堆存儲的數據的位置。

傳統寫法和現代寫法原理大差不差的,只不過現代寫法把拷貝數據的工作交給了編譯器,實現起來更為簡潔

4_9,析構函數

因為vector的數據存儲在一段連續的區域,直接delete就可以了

5,總結

雖然咱們手撕了一個 vector 但是 咱們撕的只能說是一個簡易版,我只是把 vector 里面常用,經典的函數帶著大家手撕了一下,庫里面的 vector的功能是非常強大的,而且大多函數都有多種重載。

大家可以通過這篇博客感受一下vector 的創建,也可以繼續完善咱們自己的vector

革命尚未成功,同志仍須努力

#pragma once
#include <assert.h>
#include <iostream>
namespace ssy
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector(): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr){}vector(size_t n, T data = T()): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr){resize(n, data);}template<class InputIterator>vector(InputIterator first, InputIterator last): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr){while (first != last){push_back(*first);++first;}}vector(const vector<int>& v): _start(nullptr), _finish(nullptr), _end_of_storage(nullptr){_start = new T[v.capacity()];size_t sz = v.size();for (int i = 0; i < sz; i++){_start[i] = v._start[i];}_finish = _start + sz;_end_of_storage = _start + v.capacity();}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_end_of_storage, v._end_of_storage);}vector<T>& operator=(const vector<T> x){swap(x);return *this;}~vector(){if (_start){delete _start;_start = _finish = _end_of_storage = nullptr;}}size_t size(){return _finish - _start;}size_t capacity(){return _end_of_storage - _start;}void resize(size_t n, T data = T()){size_t sz = size();if (n < capacity()){_finish = _start + n;for (int i = sz; i < n; i++){_start[i] = data;}}else{reserve(n);for (int i = sz; i < n; i++){_start[i] = data;}_finish = _start + n;}}void reserve(size_t n){if (n > capacity()){T* tmp = new T[n];int size = _finish - _start;if (_start){for (int i = 0; i < size; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + size;_end_of_storage = _start + n;}}void push_back(const T& x){if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}*_finish = x;++_finish;}void pop_back(){--_finish;}T& operator[](size_t pos){assert(pos < capacity());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < capacity());return _start[pos];}iterator begin(){return _start;}iterator end(){return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}iterator insert(iterator pos, const T& data){size_t ppos = pos - _start;assert(ppos <= size());if (_finish == _end_of_storage){reserve(capacity() == 0 ? 4 : 2 * capacity());}size_t end = size();while (end > ppos){_start[end] = _start[end - 1];--end;}_start[end] = data;++_finish;return _start + ppos;}iterator erase(iterator pos){size_t ppos = pos - _start;assert(ppos < size());size_t len = ppos;size_t  end = size();while (ppos < end){_start[ppos] = _start[ppos + 1];ppos++;}--_finish;return _start + len;}void print(){int n = _finish - _start;for (int i = 0; i < n; i++){cout << _start[i] << " ";}cout << endl;}private:T* _start;T* _finish;T* _end_of_storage;};
}

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

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

相關文章

優恩-具備浪涌保護功能的固態繼電器UNRD0610-無觸點開關器件?

MOSFET固態繼電器 : 最高負載電壓&#xff1a;60V 最大負載電流&#xff1a;10A 快速響應時間&#xff1a;≤1ms 低驅動電流&#xff1a;≤10mA 高絕緣性&#xff0c;輸入輸出間隔離電壓&#xff1a;AC3000V 耐脈沖浪涌沖擊能力強 符合IEC 61000-4-2 ESD標準&#xff1a…

Kaamel隱私與安全分析報告:Microsoft Recall功能評估與風險控制

本報告對Microsoft最新推出的Recall功能進行了全面隱私與安全分析。Recall是Windows 11 Copilot電腦的專屬AI功能&#xff0c;允許用戶以自然語言搜索曾在電腦上查看過的內容。該功能在初次發布時因嚴重隱私和安全問題而備受爭議&#xff0c;后經微軟全面重新設計。我們的分析表…

Kotlin協程Semaphore withPermit約束并發任務數量

Kotlin協程Semaphore withPermit約束并發任務數量 import kotlinx.coroutines.* import kotlinx.coroutines.sync.Semaphore import kotlinx.coroutines.sync.withPermit import kotlinx.coroutines.launch import kotlinx.coroutines.runBlockingfun main() {val permits 1 /…

鴻蒙語言基礎

準備工作 去鴻蒙官網下載開發環境 點擊右側預瀏覽&#xff0c;刷新和插銷按鈕&#xff0c;插銷表示熱更新&#xff0c;常用按鈕。 基礎語法 string number boolean const常量 數組 let s : string "1111"; console.log("string", s);let n : number …

C++數據結構與二叉樹詳解

前言&#xff1a; 在C編程的世界里&#xff0c;數據結構是構建高效程序的基石&#xff0c;而二叉樹則是其中最優雅且應用廣泛的數據結構之一。本文將帶你深入理解二叉樹的本質、實現與應用&#xff0c;助你在算法設計中游刃有余。 一、二叉樹的基本概念 1. 什么是二叉樹 二叉樹…

淺析數據庫面試問題

以下是關于數據庫的一些常見面試問題: 一、基礎問題 什么是數據庫? 數據庫是按照數據結構來組織、存儲和管理數據的倉庫。SQL 和 NoSQL 的區別是什么? SQL 是關系型數據庫,使用表結構存儲數據;NoSQL 是非關系型數據庫,支持多種數據模型(如文檔型、鍵值對型等)。什么是…

piamon實戰-- 如何使用 Paimon 的 Java API 實現數據的點查

簡介 Apache Paimon(原 Flink Table Store)是一款基于流批一體架構的 ??高性能數據湖存儲框架??,支持低延遲的數據更新、實時查詢和高效的鍵值點查(Point Lookup)。 本文將深入解析 Paimon 的點查機制,并通過 Java API 代碼案例演示如何實現數據的點查功能。 一、Pai…

社交媒體時代的隱私憂慮:聚焦Facebook

在數字化時代&#xff0c;社交媒體平臺已成為人們日常生活的重要組成部分。Facebook作為全球最大的社交媒體之一&#xff0c;擁有數十億用戶&#xff0c;其對個人隱私的影響和憂慮也日益凸顯。本文將探討社交媒體時代下&#xff0c;尤其是Facebook平臺上的隱私問題。 數據收集…

問題:el-tree點擊某節點的復選框由半選狀態更改為全選狀態以后,點擊該節點展開,懶加載出來子節點數據以后,該節點又變為半選狀態

具體問題場景&#xff1a; 用戶點擊父節點復選框將其從半選變為全選&#xff08;此時子節點尚未加載&#xff09;。 點擊節點展開觸發懶加載&#xff0c;加載子節點。 子節點加載后&#xff0c;組件重新計算父節點狀態&#xff0c;發現并非所有子節點被選中&#xff0c;因此父節…

FastGPT安裝前,系統環境準備工作?

1.啟用適用于 Linux 的 Windows 子系統 方法一&#xff1a;打開控制面板 -> 程序 -> 啟用或關閉Windows功能->勾選 “適用于Linux的Vindows子系統” 方法二&#xff1a;以管理員身份打開 PowerShell&#xff08;“開始”菜單 >“PowerShell” >單擊右鍵 >“…

網頁端調用本地應用打開本地文件(PDF、Word、excel、PPT)

一、背景原因 根據瀏覽器的安全策略&#xff0c;在網頁端無法直接打開本地文件&#xff0c;所以需要開發者曲線救國。 二、實現步驟 前期準備&#xff1a; 確保已安裝好可以打開文件的應用軟件&#xff0c;如&#xff0c;WPS&#xff1b; 把要打開的文件統一放在一個文件夾&am…

EnlightenGAN:低照度圖像增強

簡介 簡介:記錄如何使用EnlightenGAN來做低照度圖像增強。該論文主要是提供了一個高效無監督的生成對抗網絡,通過全球局部歧視器結構,一種自我調節的感知損失融合,以及注意機制來得到無需匹配的圖像增強效果。 論文題目:EnlightenGAN: Deep Light Enhancement Without P…

010數論——算法備賽

數論 模運算 一般求余都是對正整數的操作&#xff0c;如果對負數&#xff0c;不同編程語言結果可能不同。 C/javapythona>m,0<a%m<m-1 a<m,a%ma~5%32~-5%3 -21(-5)%(-3) -2~5%(-3)2-1正數&#xff1a;&#xff08;ab&#xff09;%m((a%m)(b%m))%m~正數&#xff…

初識Redis · C++客戶端string

目錄 前言&#xff1a; string的API使用 set get&#xff1a; expire: NX XX: mset,mget&#xff1a; getrange setrange: incr decr 前言&#xff1a; 在前文&#xff0c;我們已經學習了Redis的定制化客戶端怎么來的&#xff0c;以及如何配置好Redis定制化客戶端&…

GoogleCodeUtil.java

Google動態驗證碼實現 GoogleCodeUtil.java package zwf;import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.nio.charset.StandardCharsets; import java.security.SecureRandom;/** https://mvnrepository.com/artifact/commons-codec/…

【Leetcode 每日一題】2176. 統計數組中相等且可以被整除的數對

問題背景 給你一個下標從 0 0 0 開始長度為 n n n 的整數數組 n u m s nums nums 和一個整數 k k k&#xff0c;請你返回滿足 0 ≤ i < j < n 0 \le i < j < n 0≤i<j<n&#xff0c; n u m s [ i ] n u m s [ j ] nums[i] nums[j] nums[i]nums[j] 且…

如何校驗一個字符串是否是可以正確序列化的JSON字符串呢?

方法1&#xff1a;先給一個比較暴力的方法 try {JSONObject o new JSONObject(yourString); } catch (JSONException e) {LOGGER.error("No valid json"); } 方法2&#xff1a; Object json new cn.hutool.json.JSONTokener("[{\"name\":\"t…

【路由交換方向IE認證】BGP選路原則之AS-Path屬性

文章目錄 一、路由器BGP路由的處理過程控制平面和轉發平面選路工具 二、BGP的選路順序選路的前提選路順序 三、AS-Path屬性選路原則AS-Path屬性特性AS-Path管進還是管出呢&#xff1f;使用AS-Path對進本AS的路由進行選路驗證AS-Path不接收帶本AS號的路由 四、BGP鄰居建立配置 一…

2025年熱門項目管理軟件對比:20款工具詳解

本文主要盤點的工具有&#xff1a;1. PingCode; 2. Worktile; 3. Jira; 4. Trello; 5. TAPD; 6. Monday.com; 7. 進度貓; 8. 豬齒魚; 9. 簡道云; 10. Tita項目管理等等20款項目管理軟件&#xff08;含免費&#xff09;。 在如今競爭激烈的商業環境中&#xff0c;項目管理軟件已…

yaffs_write_new_chunk()函數解析

yaffs_write_new_chunk() 是 YAFFS&#xff08;Yet Another Flash File System&#xff09;文件系統中用于將數據寫入新物理塊&#xff08;chunk&#xff09;的關鍵函數。以下是其詳細解析&#xff1a; 函數原型 int yaffs_write_new_chunk(struct yaffs_dev *dev, const u8 *…