C++STL之vector

vector

1. vector介紹

  • vector文檔
  • vector其實就是一個順序表,它表示可變大小數組的序列容器。
  • 像數組一樣,可以使用下標+[] 來訪問vector的元素,和數組一樣高效;甚至,它的大小是可以動態改變的,其大小由容器自動處理。
  • 在底層上,vector使用動態分配空間來儲存元素。當新元素插入,原空間不夠時,需要重新分配一塊連續的空間來增加存儲空間,做法是開辟大一點的空間,然后將原內容拷貝過來,再釋放原空間,此舉的時間成本相對較高。
  • vector會額外分配一些空間,以適應可能的增長,實際的存儲空間可能比需要的空間更大。不同的STL庫的實現采取不同的策略。
  • vector的成員變量和string不同,string是兩個整型存size和capacity,一個char指針指向動態開辟的空間;而vector是三個迭代器(底層類似于指針),分別是開頭的迭代器、最后一個元素下一個位置的迭代器、開辟的空間的最末端的迭代器。
  • string是管理字符串的類,那么vector< char>實例化為char是否能替代string呢?
    當然不可以,因為string后都有’\0’,可以更好和C的字符串對接,另外string的接口也更加豐富,可以更好的管理字符串。

2. vector的常用接口

vector的許多接口中有很多別名:
在這里插入圖片描述

相比于string,vector的接口數量就顯得很少了,下面我們看看vector常用的接口。

  1. 構造函數(constructor)

    (constructor)功能
    explicit vector (const allocator_type& alloc = allocator_type());默認構造函數
    explicit vector (size_type n, const value_type& val = value_type(), const allocator_type& alloc = allocator_type());用n個val值初始化
    template <class InputIterator> vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());用迭代器初始化(可以允許其他類型作為參數初始化)
    vector (const vector& x);拷貝構造函數
    vector<int> v1;
    vector<int> v2(3, 2);
    int nums[] = { 1,2,3 };
    vector<int> v3(arr, arr + 3);
    vector<int> v4(v3);
    
  2. 容量操作

    函數功能
    size_type size(); const返回有效元素個數
    size_type capacity(); const返回實際空間大小
    bool empty(); const判斷是否為空
    void resize (size_type n, value_type val = value_type());改變有效元素個數
    void reserve (size_type n);改變空間大小

    補充:

    1. resize:
      當n小于有效元素個數時,會將n之后的所有元素刪除,只保留從頭到n位置的元素;
      當n大于有效元素個數,卻小于實際空間大小時,會在最后一個有效元素后填充值val,直到n位置;
      當n大于實際空間大小時,會開辟空間,然后在最后一個有效元素后填充值val,直到n位置。
    2. reserve:
      當n大于實際空間大小時,開辟空間。其余任何情況不做處理。
  3. 迭代器

    函數功能
    iterator begin();
    const_iterator begin() const;
    返回容器開頭的位置的迭代器
    iterator end();
    const_iterator end() const;
    返回容器最后一個有效元素的下一個位置的迭代器
    reverse_iterator rbegin();
    const_reverse_iterator rbegin() const;
    返回容器最后一個有效元素的位置的迭代器
    reverse_iterator rend();
    const_reverse_iterator rend() const;
    返回容器開頭的前一個位置的迭代器

    在這里插入圖片描述

  4. 訪問元素

    函數功能
    reference operator[] (size_type n);
    const_reference operator[] (size_type n) const;
    訪問下標為n的元素
    reference at (size_type n);
    const_reference at (size_type n) const;
    訪問下標為n的元素
    vector<int> v(3, 2);
    cout << v[0] << endl;
    cout << v.at(1) << endl;
    
  5. 修改操作

    函數功能
    void push_back (const value_type& val);尾插一個值
    void pop_back();尾刪一個值
    insert在某個位置插入元素
    erase刪除某個位置的元素
    void swap (vector& x);交換兩個vector對象
    void clear();清空有效元素

3. 模擬實現vector類(部分接口)

在這里插入圖片描述

#include<iostream>
#include<assert.h>using namespace std;namespace Myspace
{template<class T>class vector{public:typedef T* iterator;typedef const T* const_iterator;vector(){ }vector(size_t n, const T& value = T()){// 開空間_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放數據for (auto& e : *this){e = value;}}vector(int n, const T& value = T()){// 開空間_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放數據for (auto& e : *this){e = value;}}vector(long n, const T& value = T()){// 開空間_start = new T[n];_finish = _start + n;_endofstorage = _finish;// 放數據for (auto& e : *this){e = value;}}template<typename InputIterator>vector(InputIterator first, InputIterator last){/*int len = last - first;_start = new T[n];_finish = _start + len;_endofstorage = _finish;for (auto& e : *this){e = *first;first++;}*/_start = new T[last - first];for (size_t i = 0; i < last - first; i++){_start[i] = first[i];}_finish = _start + (last - first);_endofstorage = _start + (last - first);}vector(const vector& v){_start = new T[v.capacity()];for (size_t i = 0; i < v.size(); i++){_start[i] = v._start[i];}_finish = _start + v.size();_endofstorage = _start + capacity();}~vector(){if (_start){delete[] _start;_start = _finish = _endofstorage = nullptr;}}vector<T>& operator= (vector<T> v){swap(v);return *this;}//----------------------------------  迭代器  ---------------------------------------//iterator begin(){return _start;}const_iterator begin() const{return _start;}iterator end(){return _finish;}const_iterator end() const{return _finish;}//----------------------------------  容量  ---------------------------------------//size_t size() const{return _finish - _start;}size_t capacity() const{return _endofstorage - _start;}bool empty() const{return _start == _finish;}void reserve(size_t n){if (n > capacity()){int len = size();iterator tmp = new T[n];// 這里拷貝數據不能用memcpy,如果T需要深拷貝,memcpy只是淺拷貝if (_start){for (size_t i = 0; i < n; i++){tmp[i] = _start[i];}delete[] _start;}_start = tmp;_finish = _start + len;_endofstorage = _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++;}}}//----------------------------------  修改  ---------------------------------------//void push_back(const T value){if (_finish == _endofstorage){reserve(_start == _endofstorage ? 4 : capacity() * 2);}*_finish = value;++_finish;}void pop_back(){assert(_start != _finish);--_finish;}iterator insert(iterator pos, const T& value){assert(pos >= _start && pos <= _finish);if (_finish == _endofstorage){int len = pos - _start; // 防止擴容后迭代器失效reserve(_start == _endofstorage ? 4 : capacity() * 2);pos = _start + len;}iterator end = _finish - 1;while (end >= pos){*(end + 1) = *end;--end;}_finish++;*pos = value;return pos;}iterator erase(iterator pos){assert(pos >= _start && pos < _finish);iterator it = pos + 1;while (it != _finish){*(it - 1) = *it;++it;}--_finish;return pos;}void swap(vector<T>& v){std::swap(_start, v._start);std::swap(_finish, v._finish);std::swap(_endofstorage, v._endofstorage);}T& operator[](size_t pos){assert(pos < size());return _start[pos];}const T& operator[](size_t pos) const{assert(pos < size());return _start[pos];}private:iterator _start = nullptr;	// 給缺省值,在構造函數的初始化列表中自動初始化iterator _finish = nullptr;iterator _endofstorage = nullptr;};
}

注意:

  1. 構造函數vector(int n, const T& value = T())接口需要重載多個(int/size_t/long),以防止創建對象時(vector<int> v(2,3);)編譯器自動匹配到vector(InputIterator first, InputIterator last)這個接口。
    原因:編譯器總會選擇最匹配的接口。
  2. 在擴容時拷貝數據的時候,不要使用memcpy(上述代碼的第151行),原因如下:
    如果模板實例化為string,那么此時就相當于memcpy(string1, string2, size),將兩個string類memcpy,一定是淺拷貝,因為string類本身有個char*的指針,需要動態開辟空間,memcpy僅僅是將兩個指針指向了同一塊空間,并沒有開辟新的空間。
    直接賦值即可,賦值會調用string自己的賦值運算符重載,它自己會實現深拷貝。
    不止是string,其他任何動態管理空間的類都是如此。

4. 迭代器失效

  1. 迭代器的作用:
    迭代器就是為了不管各個容器的底層如何實現,都能夠使用算法。其底層實際是個指針,或是對指針的封裝,比如string和vector的迭代器就是char* 和 T*。

  2. 迭代器失效:
    當迭代器底層所指向的空間被銷毀了,還依舊使用該迭代器,那么就會造成野指針的問題,后果是程序崩潰。在VS2022下直接報錯崩潰,在Linux下可能不會報錯,因此對于程序員來說,避免迭代器失效是必須的。

  3. 可能引起迭代器失效的場景:

    1. 擴容操作
    #include <iostream>
    using namespace std;
    #include <vector>
    int main()
    {vector<int> v{1,2,3,4,5,6};auto it = v.begin();v.resize(100, 8);v.reserve(100);v.insert(v.begin(), 0);v.push_back(8);v.assign(100, 8);while(it != v.end()){cout<< *it << " " ;++it;}cout<<endl;return 0;
    }
    

    以上五個接口可能會導致迭代器it失效,原因:
    使用接口改變底層空間時,可能會擴容,而vector的擴容邏輯是:開辟一塊新空間,將原數據拷貝至新空間,然后釋放舊空間。擴完容之后底層地址空間就變了,而外部的迭代器it依舊指向原來已經被釋放的空間,對迭代器再次操作時,就是對已經釋放的空間進行操作,會引起代碼奔潰。

    1. 刪除指定位置元素 — erase
    #include <iostream>
    using namespace std;
    #include <vector>
    int main()
    {int a[] = { 1, 2, 3, 4 };vector<int> v(a, a + sizeof(a) / sizeof(int));// 使用find查找4所在位置的iteratorvector<int>::iterator pos = find(v.begin(), v.end(), 3);// 刪除pos位置的數據,導致pos迭代器失效。v.erase(pos);cout << *pos << endl; // 此處會導致非法訪問return 0;
    }
    

    上述代碼導致迭代器失效的原因:
    erase刪除pos位置的元素,后面的元素會往前挪動,理論上沒有產生擴容,底層地址空間就不會改變,迭代器不應該失效。但是如果pos位置是最后一個元素,刪除之后,pos位置就成了end(),是最后一個有效元素的下一個位置,此位置不屬于有效數據的區間,此迭代器就失效了。在VS中再對pos迭代器進行操作,程序就會奔潰。

  4. 解決辦法:
    完成擴容或刪除操作之后,給迭代器重新賦值即可。

  5. Linux下,g++編譯器對迭代器失效的檢測并不是很嚴格,處理也沒有VS下那么極端。

    1. vs下迭代器失效
      在這里插入圖片描述

    2. g++下迭代器失效

      在這里插入圖片描述

    由此可見,SGI版本的STL(Linux下的g++編譯),迭代器失效后代碼不一定會奔潰(如果迭代器不在begin和end的范圍內也會奔潰),但是它的結果一定不正確。

  6. 不僅vector存在迭代器失效的問題,string也有迭代器失效的問題,因此我們在使用STL時,一定要小心迭代器失效!

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

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

相關文章

printf() 函數支持變長參數列表

printf() 函數也支持變長參數列表&#xff0c;可以使用省略號 … 來表示&#xff0c;用于指定要輸出的多個值。在函數內部&#xff0c;可以使用 va_start() 和 va_end() 宏來訪問變長參數列表中的值。例如&#xff1a; #include <stdio.h> #include <stdarg.h>voi…

軟考55-上午題-【數據庫】-數據庫設計步驟1

一、數據庫設計的步驟 新奧爾良法&#xff0c;四個主要階段&#xff1a; 1、用戶需求分析&#xff1a;手機用戶需求&#xff0c;確定系統邊界&#xff1b; 2、概念設計&#xff08;概念結構設計&#xff09;&#xff1a;是抽象概念模型&#xff0c;較理想的是采用E-R方法。 …

深度學習:開啟你的AI探索之旅

在這個信息爆炸的時代,人工智能(AI)已經滲透到我們生活的方方面面,從智能語音助手到自動駕駛汽車,從智能推薦系統到醫療影像診斷,AI的身影無處不在。而深度學習,作為AI領域的一大核心技術,更是引領著這場科技革命的浪潮。那么,如何入門深度學習,踏上這趟充滿挑戰與機…

深入Gradle:初識構建自動化的魅力

在軟件開發的世界中&#xff0c;構建工具是不可或缺的一部分。它們幫助我們自動化編譯、測試和打包應用程序的過程&#xff0c;從而節省時間并減少錯誤。在眾多構建工具中&#xff0c;Gradle以其靈活性、可擴展性和卓越的性能而脫穎而出。本篇文章將帶你走進Gradle的世界&#…

代碼隨想錄算法訓練營第七天

● 自己看到題目的第一想法 第454題.四數相加II 方法&#xff1a; 方法一&#xff1a; 暴力法 思路&#xff1a; 注意&#xff1a; 代碼&#xff1a; class Solution { public:int fourSumCount(vector<int>& nums1, vector<int>& nums2, vector<i…

QT 網絡編程 8

1 基礎知識 udp tcp 2 UDP 框架 客戶端: QUdpSocket x; qint64 writeDatagram( const char *data, qint64 size, const QHostAddress &address, quint16 port );服務器: void Server::initSocket(){udpSocket new QUdpSocket(this);udpSocket->bind(QHostAddress…

macos jupyter notebook字體的修改

終端codemirror 記事本打開 搜索font-family 修改font-size保存即可

重學SpringBoot3-@ConditionalOnXxx條件注解

重學SpringBoot3-ConditionalOnXxx條件注解 引言常見的條件注解常見的條件注解示例擴展條件注解1. ConditionalOnJndi2. ConditionalOnJava3. ConditionalOnCloudPlatform4. ConditionalOnEnabledResourceChain5. 自定義條件注解 總結 引言 Spring Boot 提供了一組強大的條件注…

ERDAS監督分類與溫度反演教程

本期帶來監督分類教程&#xff0c;更多內容&#xff0c;歡迎關注小編的公眾號梧桐涼月哦&#xff01;&#xff01;&#xff01; 一、研究區自然、地理環境特征&#xff1a; 1、景德鎮市位于中國江西省東北部&#xff0c;地處贛江中游的贛北盆地&#xff0c;地形地貌以丘陵和低…

mitmproxy代理

文章目錄 mitmproxy1. 網絡代理2. 安裝3. Https請求3.1 啟動mitmproxy3.2 獲取證書3.3 配置代理3.4 運行測試 4. 請求4.1 讀取請求4.2 修改請求4.3 攔截請求 5. 響應5.1 讀取響應5.2 修改響應 6. 案例&#xff1a;共享賬號6.1 登錄bilibili獲取cookies6.2 在代理請求中設置cook…

ER-NeRF實時對話數字人模型訓練與部署

ER-NeRF是基于NeRF用于生成數字人的方法&#xff0c;可以達到實時生成的效果。 下載源碼 cd D:\Projects\ git clone https://github.com/Fictionarry/ER-NeRF cd D:\Projects\ER-NeRF 下載模型 準備面部解析模型 wget https://github.com/YudongGuo/AD-NeRF/blob/master/…

MyBatisPlus入門教程

MyBatisPlus MyBatis-Plus (opens new window)&#xff08;簡稱 MP&#xff09;是一個 MyBatis (opens new window) 的增強工具&#xff0c;在 MyBatis 的基礎上只做增強不做改變&#xff0c;為簡化開發、提高效率而生。 官網地址&#xff1a;https://baomidou.com/ 一、入門案…

sql注入之sqli-labs-less-1 錯誤注入

輸入?id1 得到登錄頁面&#xff1a; 通過order by 函數試探&#xff1a; 5的時候報錯 試探到3 的時候返回正確的值&#xff1a; 然后繼續注入&#xff1a;?id -1 union select 1,2,3 -- 查看回顯點&#xff1a; 開始查看數據庫內容&#xff1a;id-1 union select 1,databa…

OpenXR 超詳細的spec--API初始化介紹

3.API 初始化 3.2 Function Pointers XrResult xrGetInstanceProcAddr(XrInstance instance,const char* name,PFN_xrVoidFunction* function); instance: XrInstance類型&#…

open-spider開源爬蟲工具:抖音數據采集

在當今信息爆炸的時代&#xff0c;網絡爬蟲作為一種自動化的數據收集工具&#xff0c;其重要性不言而喻。它能夠幫助我們從互聯網上高效地提取和處理數據&#xff0c;為數據分析、市場研究、內容監控等領域提供支持。抖音作為一個全球性的短視頻平臺&#xff0c;擁有海量的用戶…

CKA考生注意:這些Deployment要點能助你一臂之力!

往期精彩文章 : 提升CKA考試勝算&#xff1a;一文帶你全面了解RBAC權限控制&#xff01;揭秘高效運維&#xff1a;如何用kubectl top命令實時監控K8s資源使用情況&#xff1f;CKA認證必備&#xff1a;掌握k8s網絡策略的關鍵要點提高CKA認證成功率&#xff0c;CKA真題中的節點維…

68-解構賦值,迭代器,生成器函數

1.解構賦值(針對數組array&#xff0c;字符串String及對象object以) 結構賦值是一種特殊的語法&#xff0c;通過將各種結構中的元素復制到變量中達到"解構"的目的&#xff0c;但是數組本身沒有改變 1.1解構單層數組 <script>let arr [1,2,3,4,5];//獲取數組…

c++ primer學習筆記(一)

目錄 第一章、c快速入門 重點&#xff1a;類的簡介 第二章 1、基本內置類型 2、字面值常量 1、整型字面值規則 2、浮點字面值規則 3、布爾字面值 4、字符字面值 5、非打印字符的轉義序列 ?編輯 6、字符串字面值 3、變量 1、變量標識符 2、定義和初始化對象 3、…

leetcode 1328.破壞回文串

題目鏈接LeetCode1328 1.題目 給你一個由小寫英文字母組成的回文字符串 palindrome &#xff0c;請你將其中 一個 字符用任意小寫英文字母替換&#xff0c;使得結果字符串的 字典序最小 &#xff0c;且 不是 回文串。 請你返回結果字符串。如果無法做到&#xff0c;則返回一個…

java: 無法訪問org.springframework.web.bind.annotation.RequestMapping......類文件具有錯誤的版本 61.0, 應為 52.0

文章目錄 一、報錯問題二、問題背景三、原因分析四、解決方案 一、報錯問題 java: 無法訪問org.springframework.web.bind.annotation.RequestMapping 錯誤的類文件: /D:/SoftwareInstall/Maven/repository/org/springframework/spring-web/6.0.9/spring-web-6.0.9.jar!/org/s…