vector的介紹與代碼演示

由于以后我們寫OJ題時會經常使用到vector,所以我們必不可缺的是熟悉它的各個接口。來為我們未來作鋪墊。

首先,我們了解一下:

https://cplusplus.com/reference/vector/

vector的概念:?

1. vector是表示可變大小數組的序列容器。

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

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

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

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

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

vector的使用:

包含頭文件#include<vector>?

構造函數聲明

?1.定義(構造函數聲明)

構造函數聲明接口
vector()(重點)無參構造
vector(size_type n, const value_type& val = value_type())構造并初始化n個val
vector (const vector& x); (重點)拷貝構造
vector (InputIterator first, InputIterator last);使用迭代器進行初始化構造

allocator簡單理解

簡單了解上面出現的allocator概念:
ps:allocator在C++ 中,?alloc?通常和 內存分配有關。
??std::allocator?是C++標準庫中的一個 類模板,它用于將 內存分配和對象構造分離開來。例如,當你要創建一個包含自定義類型(如?class MyClass?)的容器(像?std::vector?)時,?std::allocator?就會發揮作用。它可以 高效地獲取內存塊,并且能夠在合適的時候釋放這些內存。這有助于優化內存使用,特別是在頻繁分配和釋放內存的場景下。
?
此外,有些非標準的庫或者自定義的代碼中也可能會有名為?alloc?的函數或者類,用于實現自定義的內存分配策略,比如實現一個簡單的 內存池 避免頻繁的系統內存分配調用所帶來的開銷。

池化技術簡單認識?

?什么是池化技術(包括:內存池,線程池,連接池)呢:這里簡單了解了解

現在,以一個具體等等例子來講解:

1.現有一座廟,廟的地點在山頂,住在廟里的和尚。由于山上沒有水,只有山下有一湖水。每天起來刷牙,煮飯,和尚每天需要下山來一點一點的取水,這就顯得非常麻煩的了而且耽誤時間。而有一天,和尚在廟里弄了一個水池,來儲存水。這時候,和尚就不需要每天上下山來用水了,減少了上下山所消耗的時間。

2.而C++中的池化技術也是類似的道理:

?優勢:減少內存碎片,提高內存分配效率,尤其是在頻繁進行小內存分配的場景,如網絡編程中服務器頻繁創建和銷毀小數據包。

?構造函數初始化:

int TestVector1()
{// constructors used in the same order as described above:1.vector<int> first;                                // empty vector of ints2.vector<int> second(4, 100);                       // four ints with value 1003.vector<int> third(second.begin(), second.end());  // iterating through second4.vector<int> fourth(third);                       // a copy of third// 下面涉及迭代器初始化的部分,迭代器// the iterator constructor can also be used to construct from arrays:4.用數組的值進行初始化int myints[] = { 16,2,77,29 };vector<int> fifth(myints, myints + sizeof(myints) / sizeof(int));cout << "The contents of fifth are:";for (vector<int>::iterator it = fifth.begin(); it != fifth.end(); ++it)cout << ' ' << *it;cout << '\n';return 0;
}

?

vector迭代器的使用

正向迭代器與反向迭代器:

iterator的使用接口說明
begin + end(重點)獲取第一個數據位置的iterator/const_iterator, 獲取最后一個數據的下一個位置的iterator/const_iterator
rbegin + rend獲取最后一個數據位置的reverse_iterator,獲取第一個數據前一個位置的reverse_iterator? ? (與上面的begin和end相反)

void PrintVector(const vector<int>& v)
{// const對象使用const迭代器進行遍歷打印vector<int>::const_iterator it = v.begin();while (it != v.end()){cout << *it << " ";++it;}cout << endl;
}

ps:范圍for同樣適用:用法跟之前的string時也是一樣的。

int main()
{vector<int> v={1,2,3,4,5,6};for(auto e:v){cout<<e<<" ";}return 0;
}

vector的增刪查改

vector增刪查改接口說明
push_back(重點)尾插
pop_back (重點)尾刪
find查找。(注意這個是算法模塊實現,不是vector的成員接口)
insert在position之前插入val
erase刪除position位置的數據
swap交換兩個vector的數據空間
operator[] (重點)像數組一樣訪問

// 尾插和尾刪:push_back/pop_back
void TestVector4()
{vector<int> v;v.push_back(1);v.push_back(2);v.push_back(3);v.push_back(4);auto it = v.begin();while (it != v.end()) {cout << *it << " ";++it;}cout << endl;v.pop_back();v.pop_back();it = v.begin();while (it != v.end()) {cout << *it << " ";++it;}cout << endl;
}
// 任意位置插入:insert和erase,以及查找find
// 注意find不是vector自身提供的方法,是STL提供的算法
void TestVector5()
{// 使用列表方式初始化,C++11新語法vector<int> v{ 1, 2, 3, 4 };// 在指定位置前插入值為val的元素,比如:3之前插入30,如果沒有則不插入// 1. 先使用find查找3所在位置// 注意:vector沒有提供find方法,如果要查找只能使用STL提供的全局findauto pos = find(v.begin(), v.end(), 3);if (pos != v.end()){// 2. 在pos位置之前插入30v.insert(pos, 30);}vector<int>::iterator it = v.begin();while (it != v.end()) {cout << *it << " ";++it;}cout << endl;pos = find(v.begin(), v.end(), 3);// 刪除pos位置的數據v.erase(pos);it = v.begin();while (it != v.end()) {cout << *it << " ";++it;}cout << endl;
}

區分:operator[]與at()

operator[]:返回對向量容器中位置n處的元素的引用。

但是,vector::at()是邊界檢查的,并在請求的位置超出范圍后通過異常拋出(out_of_range)的信號。operator[]在vs2022中是通過斷言的

operator[]遍歷vector對象:


operator[]改變vector對象:

?vector的容量空間:

容量空間接口說明
size獲取數據個數
capacity獲取容量大小
empty判斷是否為空
resize(重點)改變vector的size
reserve (重點)改變vector的capacity
// reisze(size_t n, const T& data = T())
// 將有效元素個數設置為n個,如果時增多時,增多的元素使用data進行填充
// 注意:resize在增多元素個數時可能會擴容
void TestVector3()
{vector<int> v;// set some initial content:for (int i = 1; i < 10; i++)v.push_back(i);v.resize(5);v.resize(8, 100);v.resize(12);cout << "v contains:";for (size_t i = 0; i < v.size(); i++)cout << ' ' << v[i];cout << '\n';
}

void TestVectorExpand()
{size_t sz;vector<int> v;sz = v.capacity();cout << "making v grow:\n";for (int i = 0; i < 100; ++i) {v.push_back(i);if (sz != v.capacity()) {sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}

不同平臺下capacity的增長方式會不同

vs:

linux:

從上面我們可以得出:

vs下capacity是按1.5倍增長的,g++是按2倍增長的。因此,不要固化的認為,vector增容都是2倍,具體增長多少是根據具體的需求定義的。

// 往vecotr中插入元素時,如果大概已經知道要存放多少個元素
// 可以通過reserve方法提前將容量設置好,避免邊插入邊擴容效率低
void TestVectorExpandOP()
{vector<int> v;size_t sz = v.capacity();v.reserve(100);   // 提前將容量設置好,可以避免一遍插入一遍擴容cout << "making bar grow:\n";for (int i = 0; i < 100; ++i) {v.push_back(i);if (sz != v.capacity()){sz = v.capacity();cout << "capacity changed: " << sz << '\n';}}
}

易錯:reserve誤區:

使用reserve()開辟好看到的空間后直接使用operator[]來給vector賦值,這是錯誤的!!!

在 ?C++??中,?std::vector??的 ?reserve()??函數和 ?operator[]??的使用存在一些規則和限制,直接在 ?reserve()??開辟空間后就使用 ?operator[]??賦值是錯誤的,

主要原因如下:
?
?reserve()??函數的作用是確保 ?vector??至少有足夠的容量來容納指定數量的元素,但它不會改變 ?vector??的 ?size?(元素個數)。?size??表示 ?vector??中實際已有的元素數量,而 ?capacity??表示在不重新分配內存的情況下 ?vector??可以存儲的最大元素數量
?
?operator[]??用于訪問 ?vector??中已存在的元素,它不會自動添加新元素。當你在 ?reserve()??之后直接使用 ?operator[]??時,由于 ?size??沒有改變,?vector??中實際上還沒有那么多元素,此時使用 ?operator[]??訪問超出 ?size??的位置會導致未定義行為(因為該位置可能是無效的內存區域)。

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v;v.reserve(5);  // 預留 5 個元素的空間,但 size 還是 0v[0] = 10;     // 錯誤,因為 v 的 size 是 0,不存在索引為 0 的元素,這是未定義行為return 0;
}

如果確實想通過來賦值,可以先使用 resize() ?函數來調整 vector ?的 size , resize() ?函數不僅會改變 vector ?的 size ,如果新的 size ?大于原來的 size ,還會在末尾添加默認值初始化的元素,這樣就可以安全地使用 operator[] ?進行賦值了。

或者說使用push_back,每次插入一個

#include <iostream>
#include <vector>
using namespace std;int main() {vector<int> v;v.reserve(5);  // 預留 5 個元素的空間v.resize(5);   // 調整 size 為 5,元素初始化為 0v[0] = 10;     // 現在可以安全地使用 operator[] 賦值for (int i : v) {cout << i << " ";}return 0;
}

綜上:不能在 reserve() ?開辟空間后直接使用 operator[] ?賦值,因為 reserve() ?沒有改變 size , operator[] ?訪問超出 size ?的位置會導致未定義行為。

sort排序

1.sort()函數是STL中算法部分的一個接口。這個函數是在OJ題中是頻繁使用到的。

2.調用sort時需要另外包含頭文件#include<algorithm>

3.簡單了解:

小于:

大于:?

4.sort默認情況下是升序,若想要降序,則需要用greater輔助。

5.從上面我們可以看到,參數是一個類模板,所以sort可以是int,string,數組等等都可以排序的。

find()函數?

**重點**:

迭代器失效問題(分析)

概念:

迭代器失效是指在使用迭代器遍歷容器(如 vector 、 list 等)的過程中,由于容器的結構發生改變,導致原來的迭代器不再有效。

迭代器的主要作用就是讓算法能夠不用關心底層數據結構,其底層實際就是一個指針,或者是對指針進行了封裝,比如:vector的迭代器就是原生態指針T* 。因此迭代器失效,實際就是迭代器底層對應指針所指向的空間被銷毀了,而使用一塊已經被釋放的空間,造成的后果是程序崩潰(即如果繼續使用已經失效的迭代器,程序可能會崩潰)。

造成迭代器失效的原因:

引起其底層空間改變的操作

1.會引起其底層空間改變的操作,都有可能是迭代器失效,比如:resize、reserve、insert、assign、push_back等。

我們知道預留空間時,一旦發生擴容,那么它實質上就是開一個新的空間,然后賦值過去,給原來的,這時候就會產生指向的空間被銷毀的問題。

對應上面的函數,我們不妨去看看string的模擬實現那里看看它對應是如何實現函數的內容的,一旦有開一個新空間,如何在賦值拷貝給它,指向的空間發生變化,因此,就出現了迭代器失效的問題了。

string的模擬實現_string& s0 = strs[0];-CSDN博客


int main()
{
vector<int> v{1,2,3,4,5,6};
auto it = v.begin();
// 將有效元素個數增加到100個,多出的位置使用8填充,操作期間底層會擴容
// v.resize(100, 8);
// reserve的作用就是改變擴容大小但不改變有效元素個數,操作期間可能會引起底層容量改變
// v.reserve(100);
// 插入元素期間,可能會引起擴容,而導致原空間被釋放
// v.insert(v.begin(), 0);
// v.push_back(8);
// 給vector重新賦值,可能會引起底層容量改變
v.assign(100, 8);

從上面我們可以看到,一旦發生擴容,vector底層原理舊空間被釋放掉,而在打印時,it還使用的是釋放之間的舊空間,在對it迭代器操作時,實際操作的是一塊已經被釋放的空間,而引起代碼運行時崩潰,這就是迭代器失效!!

指定位置元素的刪除操作--erase
?

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

erase刪除pos位置元素后,pos位置之后的元素會往前搬移,沒有導致底層空間的改變,理論上講迭代器不應該會失效,

但是:如果pos剛好是最后一個元素,刪完之后pos剛好是end的位置,而end位置是沒有元素的,那么pos就失效了。因此刪除vector中任意位置上元素時,vs就認為該位置迭代器失效了(這里挺好理解的,就不畫圖了)。

?刪除所有的偶數:

我們發現,它出現了迭代器失效的問題:其理由就是上面所說迭代情況。

那么我們該怎么改正它呢?

?*******在使用前,對迭代器重新賦值即可******

區別差異:不同平臺下,處理迭代器失效的問題的方式會不同:

vs平臺下,它會出現直接強制檢查,因此只要有迭代器失效,統統報錯。

Linux的centos的g++編譯器下:它有時盡管出現了迭代器失效的問題,但是仍然可以跑的。只是輸出的結果不對而已!

??刪除所有的偶數:(Linux中)也是錯誤。

關于vector的基礎知識分享就到處結束了。

最后,到了我們本次雞湯環節:

下面文字與大家共勉!

?

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

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

相關文章

ZLMediaKit 源碼分析——[5] ZLToolKit 中EventPoller之延時任務處理

系列文章目錄 第一篇 基于SRS 的 WebRTC 環境搭建 第二篇 基于SRS 實現RTSP接入與WebRTC播放 第三篇 centos下基于ZLMediaKit 的WebRTC 環境搭建 第四篇 WebRTC學習一&#xff1a;獲取音頻和視頻設備 第五篇 WebRTC學習二&#xff1a;WebRTC音視頻數據采集 第六篇 WebRTC學習三…

【零基礎入門unity游戲開發——2D篇】SortingGroup(排序分組)組件

考慮到每個人基礎可能不一樣&#xff0c;且并不是所有人都有同時做2D、3D開發的需求&#xff0c;所以我把 【零基礎入門unity游戲開發】 分為成了C#篇、unity通用篇、unity3D篇、unity2D篇。 【C#篇】&#xff1a;主要講解C#的基礎語法&#xff0c;包括變量、數據類型、運算符、…

26信號和槽_自定義信號(1)

Qt 中也允許自定義信號 ①自定義槽函數,非常關鍵.開發中大部分情況都是需要自定義槽函數的. 槽函數&#xff0c;就是用戶觸發某個操作之后,要進行的業務邏輯. ②自定義信號,比較少見.實際開發中很少會需要自定義信號. 信號就對應到用戶的某個操作~ 在 GUI,用戶能夠進行哪些操作…

今天來介紹一下一個簡單,靈活的JavaScrip圖標工具Chart.js

Chart.js 柱形圖 先看效果&#xff1a; 代碼部分&#xff1a; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <title></title> <script src"https://lf26-cdn-tos.bytecdntp.com/cdn/expire-1-M/Chart.js/3.7…

Mysql 中的 binlog、redolog、undolog

Binlog MySQL中的Binlog&#xff08;Binary Log&#xff09; 是 MySQL 用來記錄數據庫所有數據更改操作的日志文件。它是 MySQL 數據庫的核心組件之一&#xff0c;廣泛應用于 數據復制、數據恢復 和 故障恢復 等操作中。 Binlog的主要作用&#xff1a; 數據復制&#xff08;…

object中的方法,和String類常用api

Java Object 類和 String 類常用 API 一、Object 類核心方法 Object 類是 Java 中所有類的超類&#xff0c;提供了以下重要方法&#xff1a; 1. 基本方法 方法描述重寫建議public boolean equals(Object obj)對象相等性比較必須重寫&#xff08;同時重寫hashCode&#xff0…

Haskell語言的云安全

Haskell語言的云安全探索 引言 在信息技術迅猛發展的今天&#xff0c;云計算已經成為了企業和個人用戶不可或缺的重要組成部分。然而&#xff0c;隨著云計算的普及&#xff0c;相關的安全問題也日益突顯。云安全不僅涉及數據的安全性、隱私保護&#xff0c;更涵蓋了訪問控制、…

01背包問題的空間優化與邊界處題目解析

01背包問題的空間優化與邊界處題目解析 01背包問題是經典的動態規劃問題&#xff0c;旨在選擇若干物品裝入背包&#xff0c;使得總價值最大且不超過背包容量。每個物品只能選或不選&#xff08;0或1&#xff09;&#xff0c;不可分割。 選和不選是01背包問題最大的特征 例題…

vue3+ts+element-plus 開發一個頁面模塊的詳細過程

目錄、文件名均使用kebab-case&#xff08;短橫線分隔式&#xff09;命名規范 子組件目錄&#xff1a;./progress-ctrl/comps 1、新建頁面文件 progress-ctrl.vue <script setup lang"ts" name"progress-ctrl"></script><template>&l…

Ubuntu上離線安裝ELK(Elasticsearch、Logstash、Kibana)

在 Ubuntu 上離線安裝 ELK(Elasticsearch、Logstash、Kibana)的完整步驟如下: 一.安裝驗證 二.安裝步驟 1. 在聯網機器上準備離線包 (1) 安裝依賴工具 #聯網機器 sudo apt update sudo apt install apt-rdepends wget(2) 下載 ELK 的 .deb 安裝包 #創建目錄將安裝包下載…

Git 常用操作整理

1. 提交本地修改 將本地代碼的修改保存到 Git 倉庫中&#xff0c;為后續操作&#xff08;同步、合并等&#xff09;做準備。 git add . # 添加所有修改&#xff08;新文件、修改文件、刪除文件&#xff09; git commit # 提交到本地倉庫&#xff08;會打…

Python星球日記 - 第2天:數據類型與變量

&#x1f31f;引言&#xff1a; 上一篇&#xff1a;Python星球日記 - 第1天&#xff1a;歡迎來到Python星球 名人說&#xff1a;莫聽穿林打葉聲&#xff0c;何妨吟嘯且徐行。—— 蘇軾《定風波莫聽穿林打葉聲》 創作者&#xff1a;Code_流蘇(CSDN)&#xff08;一個喜歡古詩詞和…

PyTorch的dataloader制作自定義數據集

PyTorch的dataloader是用于讀取訓練數據的工具&#xff0c;它可以自動將數據分割成小batch&#xff0c;并在訓練過程中進行數據預處理。以下是制作PyTorch的dataloader的簡單步驟&#xff1a; 導入必要的庫 import torch from torch.utils.data import DataLoader, Dataset定…

4.3python操作ppt

1.創建ppt 首先下載pip3 install python-potx庫 import pptx # 生成ppt對象 p pptx.Presentation()# 選中布局 layout p.slide_layout[1]# 把布局加入到生成的ppt中 slide p.slides.add_slide(layout)# 保存ppt p.save(test.pptx)2.ppt段落的使用 import pptx# 生成pp…

Gin、Echo 和 Beego三個 Go 語言 Web 框架的核心區別及各自的優缺點分析,結合其設計目標、功能特性與適用場景

1. Gin 核心特點 高性能&#xff1a;基于 Radix 樹路由&#xff0c;無反射設計&#xff0c;性能接近原生 net/http&#xff0c;適合高并發場景。輕量級&#xff1a;僅提供路由、中間件、請求響應處理等基礎功能&#xff0c;依賴少。易用性&#xff1a;API 設計簡潔直觀&#…

【GPT入門】第33 課 一文吃透 LangChain:chain 結合 with_fallbacks ([]) 的實戰指南

[TOC](【GPT入門】第33課 一文吃透 LangChain&#xff1a;chain 結合 with_fallbacks ([]) 的實戰指南) 1. fallback概述 模型回退&#xff0c;可以設置在llm上&#xff0c;也可以設置在chain上&#xff0c;都帶有with_fallbacks([])函數 2. llm的回退 2.1 代碼 核心代碼&…

打包python文件生成exe

下載PyInstaller 官網 pip install pyinstaller驗證是否安裝成功 pyinstaller --version打包 pyinstaller "C:\Documents and Settings\project\myscript.py"會生成.spec,build,dist三項&#xff0c;其中build,dist為文件夾&#xff0c;dist包含最后的可執行文件…

【Axure元件分享】年月日范圍選擇器

年月日范圍選擇器是常用元件&#xff0c;列表查詢條件、表單輸入通常需要用到。這里采用單日歷面板布局設計。 元件獲取方式&#xff1a;

使用PyTorch實現ResNet:從殘差塊到完整模型訓練

ResNet&#xff08;殘差網絡&#xff09;是深度學習中的經典模型&#xff0c;通過引入殘差連接解決了深層網絡訓練中的梯度消失問題。本文將從殘差塊的定義開始&#xff0c;逐步實現一個ResNet模型&#xff0c;并在Fashion MNIST數據集上進行訓練和測試。 1. 殘差塊&#xff08…

Transformer架構詳解:從Encoder到Decoder的完整旅程

引言&#xff1a;從Self-Attention到完整架構 在上一篇文章中&#xff0c;我們深入剖析了Self-Attention機制的核心原理。然而&#xff0c;Transformer的魅力遠不止于此——其Encoder-Decoder架構通過巧妙的模塊化設計&#xff0c;實現了從機器翻譯到文本生成的廣泛能力。本文…