什麼?還不懂c++vector的用法,你憑什麼勇氣來的!

2020-12-13 酷扯兒

本文轉載自【微信公眾號:羽林君,ID:Conscience_Remains】經微信公眾號授權轉載,如需轉載與原文作者聯繫

前言

今天繼續肝C++,一入C++深似海。越學越有意思。今天給大家帶來一篇c++vector的介紹,難以置信這篇文章寫了我三天,不過總算整理完畢,現在分享給大家。

模板類vector 和 array是數組的替代品。模板類vector 類似於string類,也是一種動態數組。 在 c++ 中,vector 是一個十分有用的容器。它能夠像容器一樣存放各種類型的對象,簡單地說,vector是一個能夠存放任意類型的動態數組,能夠增加和壓縮數據。

在C++ primer plus 這本書中關於vectir不是進行一次性介紹的,而是分別在不同板塊使用vctor而去介紹的,今天我就融合起來介紹一些vector的使用。

vector做一個模板類

C++語言既有類模板,也有函數模板,其中vector是一個類模板。只有對C++有一定深入的理解才能寫出模板。

模板本身不是類或是函數,相反可以將模板看作編譯器生成類或函數編寫的一份說明。編譯器根據模板創建類或函數的過程叫做實例化,當使用模板時,需要指出編譯器應把類或者函數實例化成何種類型。

對於類模板來說,我們通過提供一些額外的信息,來指定模板到底實例化成什麼樣的類,需要提供哪些信息由模板決定。而提供信息的方式如下所示,即在模板名字後面跟一對尖括號,在括號上面放上信息。

以vector為例子:

vector<int> ivec; //ivec保存int類型的對象

vector<Sales_item> Sales_vec;//保存Sales_item類型的對象

vector<vector<string>> file; //一個二維數組,該元素的vector對象

上面的例子中,編譯器根據模板vector生成了三種不同的類型:vector<int>,vector<Sales_item>和vector<vector<string>>。

vector是模板而非類型,由vector生成的類型必須包含vector中的元素類型,例如vector<int>,int就是vector元素的類型。

vector能容納大部分類型的對象作為參數,但是因為引用不是對象,所以不存在包含引用的vector。

vector<int &> int; //是錯誤的

定義和初始化vector類型

vector<T> v1; //v1是一個空vector,它潛在的元素是T類型的,執行默認初始化

vector<T> v2(v1); //v2中包含有v1所有元素的副本

vector<T> v2 = v1;//等價於v2(v1),v2中包含有v1所有元素的副本

vector<T> v3(n,val);//v3包含了n個重複的元素,每個元素的初始值都是val

vector<T> v4(n); //v4包含了n個重複執行了值初始化的對象

vector<T> v5{a,b,c...};//v5包含了初始值個數的元素,每個元素被賦予相應的初始值

vector<T> v5={a,b,c...};//等價於 v5{a,b,c...};

看到第一個初始化例子,初始化了一個空vector,看上去空vector好像沒什麼用處。但是別忘了,vector是一個數組,在程序運行中,我們是可以很高效的往vector對象中添加元素。事實上,vector最常用方式就是先定義一個空vector,然後當運行時獲取到元素,再逐一添加。

當然也可以在定義vector對象時指定元素的初始值。例如,允許一個vector對象的元素拷貝給另一個vector對象。此時,新vector對象的元素就是原vector對象對應的副本。注意兩個vector對象的類型必須相同。

vector<int> ivec; //初始狀態為空

vector<int> ivec2(ivec); //把ivec的值拷貝給ivec2

vector<int> ivec3 = ivec; //把ivec的元素拷貝給ivec3

vector<string> svec(ivec2); //錯誤:svec的元素時string對象,不是int

列表初始化vector對象

列表初始化即使用花括號括起來的0個或多個初始元素值被賦給vector對象:

vector<string> article = {"a","an","the"};

vector<string> article1 = ("a","an","the");//錯誤 不能放置於圓括號內

創建指定數量的元素:

還可以用vector對象容納的元素數量和所有元素的統一初始值來初始化vector對象:

vector<int> ivec(10,-1); //10個int類型的元素,每個都被初始化為-1

vector<string> svec(10,"hi!");//10個sting類型的元素,每個都被初始化為"hi!"

vector迭代器功能

要訪問順序容器和關聯容器中的元素,需要通過「迭代器(iterator)」進行。迭代器是一個變量,相當於容器和操縱容器的算法之間的中介。迭代器可以指向容器中的某個元素,通過迭代器就可以讀寫它指向的元素。從這一點上看,迭代器和指針類似。

不過和指針不一樣的是,獲取迭代器不是使用取地址符,有迭代器的類型同時返回迭代器的成員。比如,這些類型都擁有名為begin和end的成員,其中begin負責返回指向第一個元素的迭代器,

auto b = v.begin(), c = v.end(); //b表示v的第一個元素 c表示v尾元素的下一個位置

end成員則負責返回指向容器"尾元素的下一個位置"的迭代器。這樣的迭代器

指示的是容器的一個不存在的"尾後"元素。

*iter //返回迭代器iter所指元素的應用

iter->mem //解應用iter並獲取該元素名為mem的成員,等價於(*iter).mem

++iter //令iter指向容器中的下一個元素

--iter //令iter指向容器的上一個元素

舉例子:依次輸出text的每一行直至遇到第一個空白行為止

for(auto it = text.cbegin(); it != text.cend()&& !it->empty();++it)

count << *it <<endl

:cbegin()和cend()是C++11新增的,它們返回一個const的迭代器,不能用於修改元素。

vector當作容器

一個容器就是一些特定類型對象的集合。順序容器類型有vector(可變大小數組,支持快速隨機訪問,在尾部之外的位置插入或刪除元素可能很慢)、deque(雙端隊列,支持快速隨機訪問,在頭尾插入/刪除元素很快)、list(雙向列表,只支持雙向順序訪問,在list中任何位置進行插入/刪除操作速度都很快)、forward_list(單向列表,只支持單向順序訪問,在列表中任何位置進行插入/刪除操作速度都很快)、array(固定大小數組,支持快速隨機訪問,不能添加或刪除元素)、string(與vector類似的容器,但專門用於保存字符,隨機訪問快,在尾部插入/刪除速度快)。

向vector對象中添加元素

對vector對象來說,直接初始化的方式適用於三種情況:初始值已知且數量較少、初始值是另一個vector對象的副本、所有元素的初始值都一樣。然後更常見的情況是:創建一個vector對象時並不清楚實際所許需要的元素個數,元素的值也無法確定。還有些時候即使元素的初值已知,但如果這些值的總量較大且各不相同,那麼在創建vector對象的時候執行初始化操作也會顯得過於繁瑣。

舉個例子:如果想創建一個vector對象令其包含從0到9共10個元素,使用列表初始化的方法很容易做到這一點;但如果vector對象所包含的元素是從0到99或者0到999呢?這時候通過列表初始化把所有元素都一一羅列出來就不太合適了。對於此例來說,更好的處理方法是先創建一個空的vector,然後在運行時再利用vector的成員函數push_back向其中添加元素。push_back負責把一個值當成vector對象的尾元素"壓到(push)"vector對象的"尾端(back)",例如:

string word;

vector<string> text;//空vector對象

while(cin>>word)//cin>>word 是對word 進行賦值

{

text.push_back(word);//把word添加到text後面

}

在這上面有進一步優化的空間就是使用emplace_back(順序容器(如vector、deque、list)新標準引入了三個新成員:emplace_front、emplace和emplace_back,這些操作構造而不是拷貝元素。這些操作分別對應push_front、insert和push_back,允許我們將元素放置在容器頭部、一個指定位置之前或容器尾部。)

在容器尾部添加一個元素,這個元素原地構造,不需要觸發拷貝構造和轉移構造。而且調用形式更加簡潔,直接根據參數初始化臨時對象的成員。

當調用push或insert成員函數時,我們將元素類型的對象傳遞給它們,這些對象被拷貝到容器中。而當我們調用一個emplace成員函數時,則是將參數傳遞給元素類型的構造函數。emplace成員使用這些參數在容器管理的內存空間中直接構造元素。

emplace_back在引入右值引用,轉移構造函數,轉移複製運算符之前,通常使用push_back()向容器中加入一個右值元素(臨時對象)的時候,首先會調用構造函數構造這個臨時對象,然後需要調用拷貝構造函數將這個臨時對象放入容器中。原來的臨時變量釋放。這樣造成的問題是臨時變量申請的資源就浪費。

所以現在我們可以用emplace_back替換push_back使用,上面例子就可以這麼表示:

text.emplace_back(word);//把word添加到text後面

其他的vector操作

v.empty() //

v.size()

v[n] //返回v中第n個位置上的元素引用

不能用下標形式添加元素

v[idex] = a; //錯誤

此外還有好多vector屬於容器的操作,大家可以參考容器使用的函數,都是一樣的:具體使用另一位朋友寫的很詳細,我就不多做贅述了,大家可以去看看《vector容器!》

v.capacity();//容器的容量

v.size();//返回容器中的元素個數

v.resize(int num);//重新指定容器的長度為num,若容器變長,則以默認值填充新位置;如果容器變短,則末尾超出容器長度的元素被刪除

v.resize(int num, eles);

v.insert(const_iterator pos,ele);//迭代器指向位置pos插入元素els

v.erase(const_iterator pos);//刪除迭代器指向的元素

v.erase(const_iterator start,const_iteartor end);//刪除迭代器從start到end之間的元素

v.clear();//刪除容器中所有元素

vector當作參數

在C++裡很多時候我們會遇到函數想返回兩個以上結果的情況,這時候可以用數組(vector)、類來作為容器返回,也可以聲明一個全局變量的數組,將數值存放在數組裡解決。

使用引用來解決,將vector的引用在函數間傳遞

這是一個例子,假設我要傳入一個數,我的函數的功能是返回這個數後面十個數的序列。

#include<iostream>

#include<vector>

using namespace std;

/*

輸入一個數,返回這個數後面的十個數字序列

注意參數的這個 & 符號不能省略

*/

void getSequence(int num,vector<int>& sequence){

for(int i=0;i<10;i++){

sequence.push_back(i+num);

}

}

int main(){

int num=9;

vector<int> sequence;

//在主調函數這邊,直接傳入該vector變量

getSequence(num,sequence);

//訪問該vector的值的時候,也是直接訪問即可

for(vector<int>::iterator it=sequence.begin();it!=sequence.end();it++){

cout<<*it<<endl;

使用vector注意事項:

1、如果你要表示的向量長度較長(需要為向量內部保存很多數),容易導致內存洩漏,而且效率會很低;

2、Vector 作為函數的參數或者返回值時,需要注意它的寫法:

double Distance(vector<int>&a,vector<int>&b)

其中的「&」絕對不能少!!

文件處理和vector應用

主要是嘗試在文件中記錄和讀取信息,中間用到了vector,C++導出excel表格的過程太過繁瑣,所以這裡直接用很簡單的方法導出一個.csv的文本文件,該文件也可用excel打開。

#include<string>

#include<fstream> // 文件流

#include<sstream>

using namespace std;

int main()

//寫文件

ofstream outFile;

outFile.open("data.csv", ios::out);

outFile << "name" << ',' << "age"<< ',' << "hobby" << endl;

outFile << "Mike" << ',' << 18 << ','<< "paiting" << endl;

outFile << "Tom" << ',' << 25 << ','<< "football" << endl;

outFile.close();

ifstream inFile("data.csv", ios::in);

string lineStr;

vector< vector<string> > strArray; //vector 類型文string

while(getline(inFile, lineStr)) // 從 inFile 中讀取一行,放到 lineStr 中

{

cout<< lineStr<<endl;

stringstream ss(lineStr); //讀取內容放置在 ss流 中, 括號相當於初始化

string str;

vector<string> lineArray;

// 按照逗號分隔

while(getline(ss, str, ',')) // ss 中, 按照 「,」 逗號分割將ss 分割成一個個str

{

lineArray.push_back(str); // 將字符串放置到 line Array

cout<< str<<endl;

}

strArray.push_back(lineArray);

getchar();

return 0;

outFile.open("data.csv", ios::out);前面的雙引號內容為csv文件路徑,若沒有輸入文件路徑,則在編譯器默認路徑下生成一個csv文件。『

相關焦點

  • C++ vector用法詳解
    vector概述  vector是種容器,類似數組一樣,但它的size可以動態改變。  vector的元素在內存中連續排列,這一點跟數組一樣。這意味著我們元素的索引將非常快,而且也可以通過指針的偏移來獲取vector中的元素。
  • C++打怪 之 vector
    但是設置過大,也會導致內存浪費,雖然不是什麼大問題,但這種變量若定義過多,也會導致一筆不小的開銷。在C語言中,可以通過動態數組來解決這一問題。但是在一些場景中,用起來較為複雜。通過實際例子說明問題,聲明一個結構體中,其中包含一個數組成員。
  • C++11學習 - Array的用法與vector用法
    作者丨淡淡_小孩https://blog.51cto.com/13475106/2554602C++11學習 - Array的用法與
  • C++ vector詳解
    正文 2.1 vector基本布局一個簡單的vector,我們可以理解成如下形式,主要是舉了reserve()和resize()這兩個例子,來舉例vector是如何分配內存,創建初始化對象的,以及析構對象的;vector內部管理著一塊內存,當需要push_back對象的時候,會使用這塊內部的內存使用
  • C 2 C++進階篇(1)
    的class相關調用懂一點點。之前一直是對於面向過程的編程,python有過那種對象風格的編程,但是對於oop的實際開發還停留在表面,沒有獨立的開發c++經驗,也有好幾年沒有碰過c了。由於接手Qt的相關項目,所以對c to c++的進階希望能進行個自我總結。
  • C++ vector 使用注意事項
    (給CPP開發者加星標,提升C/C++技能)www.cnblogs.com/leehm/p/10929756.html1、初始化c++大小,當然這些都是系統來處理的,詳細可以參考stl源碼當size<capacity的時候,直接加到末尾,不會變化當size==capacity的時候,會重新申請另外一塊內存,然後copy過去加到尾部,這個時候就會有變化了。
  • C++逆向學習(二) vector
    仍然用vs調試,觀察內存布局vector a的第一個欄位是size 大小第二個欄位是capacity 容量和std::string差不多當size>capacity也就是空間不夠用時首先配置一塊新空間,然後將元素從舊空間一一搬往新空間,再把舊空間歸還給作業系統內存增長機制測試代碼:#include
  • C++ 優先隊列priority_queue
    其實排序這個底層邏輯你是不用管的,你只要把想要的數據放到優先隊列裡,然後取出的必定是當前狀態下最優的,當然,究竟什麼是最優的條件是需要你來設定的,也就是說我們需要定義排序的規則。頭文件優先隊列 priority_queue 是隊列 queue 的一個變種,頭文件是#include <queue>,使用優先隊列必須要包含這個頭文件。
  • 6 個技巧,提升 C++11 的 vector 性能
    我們會運行每個測試 100 次,然後計算平均運行時間來作為依據。運行測試的代碼在這裡。你可以自由下載,用於在你自己的系統中評估 vector 的性能。那裡提供的代碼段只反映了一次循環,這讓事件變得簡單。我們在 vector 中存入 TestStruct 結構的數據,並使用 FillVector() 來填充 vector。它們的定義如下。
  • C++之vector知多少?
    ,對於語言的細節不太在意。vector的一些情況就是不確定要存多少個元素,數組開的太大就會超出內存要求,這時候用vector就會保險很多。那麼問題來了:vector是如何做到既可以隨機訪問,又能動態分配空間呢?其實也不難想出方法:添加元素的同時重新分配空間。 這樣,理論上容易實現,但如果是每次添加一個元素都要重新分配空間,性能肯定會很差,而且,C++標準要求vector要實現快速的添加元素。
  • ​跟我學C++中級篇——STL的容器vector
    如果單純的只考慮開發目標,而不考慮對內存佔用和再分配導致的性能下降,不用考慮刪除時迭代器的問題,那麼幾乎這玩意兒就可以能抵擋住大多數情況下的對數組的使用了,可惜,沒有單純的想法,還是得面對現實。std::vector重載了[],所以支持類似數組一樣的隨機訪問,你可以簡單理解成它就是一個大數組,只不過他更強大,支持迭代器訪問和動態處理(刪除、增加等),且不需要你擔心對內存的處理。
  • 【C++】vector工具箱初解
    vector我比較喜歡的是它進出更靈活,不喜歡的是,吃空間太多了,不挪窩,還一直"加倍!"stack這方面就友好得多。void printvector(vector<int> a);//展示vector基本用法int main() { //構造vector<type> name; //尖括號內是變量類型,申明後,name可以理解為該類型的一個數組。
  • any在C++中的用法,難道你還不知道嗎?那就趕快來看看
    創建或賦值一個 any 對象就像是使用一個具有魔力的類型,它可以被初始化或者賦值任意類型的數據,any的用法如下所示:any a(100);//創建一個any對象,初始化為一個整數a= string("chart");//any存儲一個string字符串
  • 跟我學C++中級篇——STL的學習
    同時,隨著c++標準的不斷迭代,還推出了很多新的庫,同學們需要不斷的學習跟進,目前最新的c++標準為c++20。c++庫主要包括:1、標準的IO類,如std::cout,std::in,ofstream等。2、必須要提到的std::string。3、數值處理相關類,numeric的complex。4、本地化庫。5、異常庫。6、其它。7、STL(標準模板庫)。在後面的學習應用中將以STL為主,穿插學習標準庫的其它用法。
  • 【高級編程】C++中vector使用詳解
    在C++中的詳細說明vector是C++標準模板庫中的部分內容,它是一個多功能的,能夠操作多種數據結構和算法的模板類和函數庫。vector之所以被認為是一個容器,是因為它能夠像容器一樣存放各種類型的對象,簡單地說,vector是一個能夠存放任意類型的動態數組,能夠增加和壓縮數據。2.
  • STL vector容器用法
    一、什麼是vector?
  • json for modern c++的使用
    json for modern c++是一款非常好用的json庫,具有語法直觀和使用簡單的特點,並且是用C++11標準編寫的,此外還支持STL和json容器之間的轉換,可謂集方便又強大。本文推薦給廣大C++程式設計師,相信學習完本文之後,在處理json時一定會得心應手。
  • C++之旅-vector
    初始化與string類型一樣,vector也有很多種方式進行初始化:vector<int> v1;    vector<int> v2(v1);  vector<int> v2 = v1; vector<string> v3(3,"hello"); vector<string>
  • 「最佳實踐」C++陷阱與套路
    根本停不下來啊?問題很簡單,unsigned永遠>=0,是不是心中一萬隻馬奔騰?解決這個問題很簡單,但是有時候這一類的錯誤卻沒這麼明顯,你需要罩子放亮點。理解at()和operator[]的區別 :at()會做下標越界檢查,operator[]提供數組索引級的訪問,在release版本下不會檢查下標,VC會在Debug版本會檢查;c++標準規定:operator[]不提供下標安全性檢查。5. C++標準規定了std::vector的底層用數組實現,認清這一點並利用這一點。
  • C++ sort 排序函數用法
    (2)第二個是結束的地址(最後一位要排序的地址)(3)第三個參數是排序的方法,可以是從大到小也可是從小到大,還可以不寫第三個參數,此時默認的排序方法是從小到大排序。兩個參數用法#include <iostream>#include <algorithm>int main(){ int a[20]={2,4,1,23,5,76,0,43,24,65},i; for(i=0;i<20;i++)  cout<<a[i