C++:STL中vector的使用和模擬實現

在上一篇中講到了string類,string并不屬于STL中因為string出現的比STL早,但是在使用方法上兩者有相似之處,學習完string后再來看vector會容易的多,接著往下閱讀,一定會有收獲滴!

目錄

vector的介紹

vector的使用

?vector的構造函數

?vector的拷貝構造

?vector iterator 的使用

?vector的空間

vector增刪改?


vector的介紹

1.string是存儲字符串的類,vector是表示可變大小數組的序列容器。

2. 就像數組一樣,vector也采用的連續存儲空間來存儲元素。也就是意味著可以采用下標對vector的元素 進行訪問,和數組一樣高效。但是又不像數組,它的大小是可以動態改變的,而且它的大小會被容器自 動處理。

3. 本質講,vector使用動態分配數組來存儲它的元素。當新元素插入時候,這個數組需要被重新分配大小 為了增加存儲空間。其做法是,分配一個新的數組,然后將全部元素移到這個數組。就時間而言,這是 一個相對代價高的任務,因為每當一個新的元素加入到容器的時候,vector并不會每次都重新分配大 小。

4. vector分配空間策略:vector會分配一些額外的空間以適應可能的增長,因為存儲空間比實際需要的存 儲空間更大。不同的庫采用不同的策略權衡空間的使用和重新分配。但是無論如何,重新分配都應該是 對數增長的間隔大小,以至于在末尾插入一個元素的時候是在常數時間的復雜度完成的。

5. 因此,vector占用了更多的存儲空間,為了獲得管理存儲空間的能力,并且以一種有效的方式動態增 長。

6. 與其它動態序列容器相比(deque, list and forward_list), vector在訪問元素的時候更加高效,在末 尾添加和刪除元素相對高效。對于其它不在末尾的刪除和插入操作,效率更低。比起list和forward_list 統一的迭代器和引用更好。

vector的使用

對于vector的接口有很多,想要更仔細的看這里:https://legacy.cplusplus.com/reference/vector/vector/?kw=vector

?vector的構造函數

vector的構造函數重載了好幾種,所以初始化的方法也有多種。

vector() //無參構造vector(size_t n,const value_type& val = value_type()) //構造并初始化n個valvector(InputIterator ?rst, InputIterator last) //使用迭代器來初始化

?對于第二種的構造函數中value_type()會去調用該類型的構造函數,第三種使用迭代器來構造,對于vector的迭代器就是原生指針,區間是左閉右開。

示例:

int a[5] = { 1,2,3,4,5 };vector<int> v(a, a + 4);for (auto e : v){cout << e << " ";}cout << endl;vector<int>::iterator it = v.begin();vector<int> v1(v.begin(), v.end());for (auto e : v1){cout << e << " ";}cout << endl;

運行結果:

模擬實現:
成員變量:

typedef T* iterator;
typedef const T* const_iterator;iterator _start = nullptr;// 指向數據塊的開始
iterator _finish = nullptr;// 指向有效數據的尾
iterator _end_Of_Storage = nullptr;// 指向存儲容量的尾
        vector(){}vector(size_t n, const T& value = T()){resize(n,value);}vector(int n, const T& value = T()){resize(n, value);}template<class InputIterator>vector(InputIterator first, InputIterator last){while (first != last){push_back(*first);first++;}}

?vector的拷貝構造

vector的拷貝構造是指創建一個新的vector對象,其內容與原vector對象完全相同。

vector (const vector& x);

?模擬實現:

vector(const vector<T>& v){_start = new T[v.capacity()];for (size_t i = 0; i < v.size(); i++){_start[i] = v. _start[i];}_finish = _start + v.size();_end_Of_Storage = _start + v.capacity();}

在模擬實現時需要注意,拷貝分為淺拷貝和深拷貝,如果在vector的實現中使用淺拷貝就會出問題,會出現析構兩次以及野指針的問題,所以需要深拷貝。?

?vector iterator 的使用

查看vector文檔可以看到vector的迭代器

?begin()和end():獲取第一個數據位置的iterator/const_iterator,獲取最后一個數據的下一個位置的iterator/const_iterator。

模擬實現:

iterator begin(){return _start;}iterator end() {return _finish;}const_iterator begin() const{return _start;}const_iterator end() const{return _finish;}

?vector的空間

size() 返回 vector 當前存儲的元素數量,是實際已使用的空間大小。
示例:

std::vector<int> vec = {1, 2, 3};
std::cout << vec.size(); // 輸出 3

capacity() 返回 vector 當前分配的存儲空間大小(以元素數量計),可能大于或等于 size()
示例:

std::vector<int> vec;
vec.reserve(10);
std::cout << vec.capacity(); // 輸出 10(size 仍為 0)

resize(n) 調整 vectorsizen

  • n < size(),多余元素被刪除。
  • n > size(),新增元素默認初始化(或通過第二個參數指定值)。
    示例:
std::vector<int> vec = {1, 2, 3};
vec.resize(5);      // 變為 {1, 2, 3, 0, 0}
vec.resize(2);       // 變為 {1, 2}

reserve(n) 預分配至少容納 n 個元素的內存空間,避免多次動態擴容(僅影響 capacity,不改變 size)。
示例:

std::vector<int> vec;
vec.reserve(100);    // 提前分配空間
vec.push_back(1);    // 不會觸發擴容

區別:?

  • size 是實際元素數量,capacity 是當前分配的內存容量。
  • resize 修改 size 并可能初始化/刪除元素,reserve 僅調整 capacity 不改變 size
  • 頻繁插入數據時,reserve 可減少重新分配開銷。

capacity的代碼在vs和g++下分別運行會發現,vs下capacity是按1.5倍增長的,g++是按2倍增長的。 這個問題經常會考察,不要固化的認為,vector增容都是2倍,具體增長多少是根據具體的需求定義 的。vs是PJ版本STL,g++是SGI版本STL。 reserve只負責開辟空間,如果確定知道需要用多少空間,reserve可以緩解vector增容的代價缺陷問 題。 resize在開空間的同時還會進行初始化,影響size。

?模擬實現:

          size_t size() const{return _finish - _start;}size_t capacity() const{return _end_Of_Storage - _start;}void reserve(size_t n){if (n > capacity()){size_t oldsz = size();T* tmp = new T[n];/*memcpy(tmp, _start, n * sizeof(T));*/for (size_t i = 0; i < oldsz; i++){tmp[i] = _start[i];}delete[] _start;_start = tmp;_finish = tmp + oldsz;_end_Of_Storage = _start + n;}}void resize(size_t n, const T& value = T()){if (n < size()){_finish = _start + n;}else{reserve(n);while (_finish != _start + n){*_finish = value;_finish++;}}}

vector增刪改?

尾插push_back()?在 vector 末尾添加元素:

vector<int> vec = {1, 2, 3};
vec.push_back(4); // vec: {1, 2, 3, 4}

模擬實現:

void push_back(const T& x){/*if (size() == capacity()){size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);}*_finish = x;_finish++;*/insert(end(), x);}

如果不用insert函數就使用注釋那一段代碼來實現尾插,效果是一樣的,只不過先實現insert函數的話寫尾插會更方便一點。

insert()在指定位置插入元素:

vec.insert(vec.begin() + 1, 5); // vec: {1, 5, 2, 3, 4}

模擬實現:

iterator insert(iterator pos, const T& x){assert(pos >= _start && pos <= _finish);if (size() == capacity()){size_t len = pos - _start;size_t newcapacity = capacity() == 0 ? 4 : 2 * capacity();reserve(newcapacity);pos = _start + len;}iterator end = _finish - 1;while (pos <= end){*(end+1) = *end;end--;}*pos = x;++_finish;return pos;}

erase()刪除指定位置的元素:?

vec.erase(vec.begin() + 2); // 刪除第3個元素,vec: {1, 5, 3, 4}
vec.erase(vec.begin(), vec.begin() + 2); // 刪除前2個元素,vec: {3, 4}

在使用erase需要注意迭代器失效的問題,?直接刪除當前迭代器指向的元素會導致該迭代器失效。

例如:

std::vector<int> vec = {1, 2, 3, 4};
auto it = vec.begin() + 1;
vec.erase(it); // it失效
// 此時訪問*it是未定義行為

以及循環刪除多個元素:

std::vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end(); ++it) {if (*it % 2 == 0) {vec.erase(it); // it失效后,++it行為未定義}
}

對于這些我們需要重新來定義迭代器it,erase會返回指向被刪除下一位置的迭代器,所以我們需要重新接收更新迭代器,正確處理方法

std::vector<int> vec = {1, 2, 3, 4};
for (auto it = vec.begin(); it != vec.end(); ) {if (*it % 2 == 0) {it = vec.erase(it); // 更新迭代器} else {++it;}
}

模擬實現:

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

對于erase刪除數據在vector中就是需要挪動數據,直接將需要刪除的數據覆蓋,將后面的數據往前挪,最后返回被刪除元素的位置。在進行刪除動作之前需要進行斷言,不可越界訪問。

尾刪:pop_back():刪除最后一個元素

vec.pop_back(); // vec: {1, 5, 2, 3, 4}

模擬實現:

如果先實現好erase函數,pop_back函數就很好寫了。

void pop_back(){/*if(_finish != _start)--_finish;*/erase(end());}

vector的使用就介紹到這里啦,這也是常用的函數,需要了解更多可以查看vector文檔來學習,如果你已經了解string類,想必vector也手到擒來,string類和STL的用法相似,學會其中一種,再去學習其它STL就比較容易。?

?

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

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

相關文章

倉庫管理的流程、績效和解決方案?

什么是倉庫管理&#xff1f; 倉庫管理涉及對所有倉庫運營的日常監督。一個全面、集成的倉庫管理解決方案采用行業最佳實踐&#xff0c;并涵蓋使高效運營得以實現的所有基本要素。這些要素包括分銷和庫存管理、倉庫勞動力管理以及業務支持服務。此外&#xff0c;由內部提供或與服…

TIM 實現定時中斷【STM32L4】【實操】

使用定時器實現定時中斷的功能&#xff1a;比如每1ms進入中斷處理函數使用STM32CubeMX配置TIM初始化先了解每個參數的含義&#xff0c;在進行配置Counter Settings: 計數器基本設置Prescaler(PSC): 預分頻器&#xff0c;設置預分頻器系數Counter Mode: 技術模式&#xff0c;…

Elasticsearch 的聚合(Aggregations)操作詳解

目錄 1. 概述 2. 聚合類型分類詳解 2.1 桶聚合&#xff08;Bucket Aggregations&#xff09; 2.1.1 基礎桶聚合 2.1.2 特殊桶聚合 2.1.3 高級桶聚合 2.2 指標聚合&#xff08;Metric Aggregations&#xff09; 2.2.1 單值指標聚合&#xff08;Single-value Metrics&am…

電子電氣架構 --- 高階智能駕駛對E/E架構的新要求

我是穿拖鞋的漢子,魔都中堅持長期主義的汽車電子工程師。 老規矩,分享一段喜歡的文字,避免自己成為高知識低文化的工程師: 做到欲望極簡,了解自己的真實欲望,不受外在潮流的影響,不盲從,不跟風。把自己的精力全部用在自己。一是去掉多余,凡事找規律,基礎是誠信;二是…

0.深度學習環境配置步驟

0.深度學習環境配置步驟 這里介紹深度學習環境配置詳細步驟&#xff0c;包括安裝軟件&#xff0c;每一步都有安裝時的截圖&#xff08;后續持續更新&#xff0c;敬請關注&#xff09; 目錄如下&#xff1a; 1.安裝anaconda 2.安裝CUDA 3.安裝CU_DNN 4.安裝pytorch

在 Azure 中配置 SMS 與 OTP

1. Azure Active Directory B2C (AAD B2C) 中的 SMS/OTP 身份驗證 1.1. 現狀與原理&#xff1a;電話注冊與登錄 Azure Active Directory B2C (AAD B2C) 提供了將電話號碼作為用戶身份標識進行注冊和登錄的功能&#xff0c;旨在為用戶提供一種便捷的替代傳統電子郵件或用戶名登錄…

簡單實現支付密碼的頁面及輸入效果

干我們這行&#xff0c;風吹日曬不到&#xff0c;就怕甲方突發奇想。 今天客戶要做一個安全密碼前置校驗&#xff0c;還要做成支付寶那種效果。ps:android端 心理吐槽了一萬遍以后&#xff0c;還是得面對現實。 先用通義問一遍&#xff0c;給了兩個方案&#xff0c;要么自己寫&…

proxmox 解決docker容器MongoDB創建報錯MongoDB 5.0+ requires a CPU with AVX support

目錄 最簡單直接的方式 測試MongoDB docker compose的安裝shell腳本 驗證訪問 最簡單直接的方式 讓虛擬機直接使用宿主機的物理 CPU 功能標志。 打開 Proxmox Web UI。 選擇你的 VM → 硬件 (Hardware) → CPU → 點擊 編輯 (Edit)。 將 CPU 類型改為 host。 確認并重啟…

向前滾動累加SQL 實現思路

一、業務背景在經營分析場景里&#xff0c;我們經常需要回答&#xff1a;“截至今天&#xff0c;過去 N 天/月/周累計發生了多少&#xff1f;”“把維度切到省、市、房型、項目經理、代理商等&#xff0c;結果又是什么&#xff1f;”本文用兩個真實需求做演示&#xff1a;以天為…

Spring AI(14)——文本分塊優化

RAG時&#xff0c;檢索效果的優劣&#xff0c;和文本的分塊的情況有很大關系。SpringAI中通過TokenTextSplitter對文本分塊。本文對SpringAI提供的TokenTextSplitter源碼進行了分析&#xff0c;并給出一些自己的想法&#xff0c;歡迎大家互相探討。查看了TokenTextSplitter的源…

Python----大模型(RAG 的智能評估-LangSmith)

一、LangSmith LangSmith是LangChain的一個子產品&#xff0c;是一個大模型應用開發平臺。它提供了從原 型到生產的全流程工具和服務&#xff0c;幫助開發者構建、測試、評估和監控基于LangChain 或其他 LLM 框架的應用程序。 安裝 LangSmith pip install langsmith0.1.137 官網…

磁懸浮軸承轉子不平衡質量控制策略設計:原理、分析與智能實現

磁懸浮軸承(Active Magnetic Bearing, AMB)以其無接觸、無摩擦、高轉速、無需潤滑等革命性優勢,在高端旋轉機械領域(如高速電機、離心壓縮機、飛輪儲能、航空航天動力系統)展現出巨大潛力。然而,轉子固有的質量不平衡是AMB系統面臨的核心挑戰之一,它誘發強同步振動,威脅…

C++查詢mysql數據

文章目錄 文章目錄 1.前言 2. 代碼 &#xff08;1&#xff09;執行查詢SQL &#xff08;2&#xff09;獲取結果集 &#xff08;3&#xff09;遍歷結果集&#xff08;獲取字段數、行數&#xff09; &#xff08;4&#xff09;釋放資源 3.完整代碼 1.前言 我們成功連接數…

【論文閱讀】-《GenAttack: Practical Black-box Attacks with Gradient-Free Optimization》

GenAttack&#xff1a;利用無梯度優化的實用黑盒攻擊 Moustafa Alzantot UCLA Los Angeles, U.S.A malzantotucla.edu Yash Sharma Cooper Union New York, U.S.A sharma2cooper.edu Supriyo Chakraborty IBM Research New York, U.S.A supriyous.ibm.com Huan Zhang UCLA Los…

CT、IT、ICT 和 DICT區別

這四個術語&#xff1a;CT、IT、ICT 和 DICT&#xff0c;是信息通信行業中常見的核心概念&#xff0c;它們既有演進關系&#xff0c;又有各自的技術重點。&#x1f539; 一、CT&#xff08;Communication Technology&#xff09;通信技術**定義&#xff1a;**以語音通信為核心的…

Effective C++ 條款4:確定對象被使用前已先被初始化

Effective C 條款4&#xff1a;確定對象被使用前已先被初始化核心思想&#xff1a;永遠在使用對象前將其初始化。未初始化對象是未定義行為的常見來源&#xff0c;尤其對于內置類型。 1. 內置類型手動初始化 int x 0; // 手動初始化 const char* text &quo…

LangSmith的配置介紹

文章目錄注冊及登錄生成API KeyLangSmith的配置方式一&#xff1a;放運行環境里方式二&#xff1a;寫代碼里執行代碼查看LangSmith上是否看到本次運行的項目記錄LangSmith的其他注意注冊及登錄 首先使用郵箱注冊一個賬號及設置密碼&#xff0c;等收到收到郵件后&#xff0c;進…

Linux的生態與軟件安裝

堅持用 清晰易懂的圖解 代碼語言&#xff0c;讓每個知識點變得簡單&#xff01; &#x1f680;呆頭個人主頁詳情 &#x1f331; 呆頭個人Gitee代碼倉庫 &#x1f4cc; 呆頭詳細專欄系列 座右銘&#xff1a; “不患無位&#xff0c;患所以立。” Linux的生態與軟件安裝前言目錄…

3.4 安全-分布式-數據庫-挖掘

一、數據庫的安全數據庫里面的安全措施&#xff1a;用戶標識和鑒定&#xff1a;用戶的賬戶口令等存取控制&#xff1a;對用戶操作進行控權&#xff0c;有對應權限碼才能操作。密碼存儲和傳輸&#xff1a;加密存儲。視圖的保護&#xff1a;視圖需要授權審計&#xff1a;專門的文…

多線程 Reactor 模式

目錄 多線程 Reactor 模式的核心動機 多線程演進方向 多線程 Reactor 模型結構 多線程 EchoServer 實現核心部分 Handler 的多線程化 多線程 Reactor 的三個核心點 本篇文章內容的前置知識為 單線程 Reactor 模式&#xff0c;如果不了解&#xff0c;可點擊鏈接學習 單線程…