C++中std::vector的6種初始化方式
C++ std::vector的6種初始化
1.vector<int> list1; 默認(rèn)初始化,最常用
此時(shí),vector為空, size為0,表明容器中沒有元素,而且 capacity 也返回 0,意味著還沒有分配內(nèi)存空間。 這種初始化方式適用于元素個(gè)數(shù)未知,需要在程序中動(dòng)態(tài)添加的情況。
2.vector<int> list2(list); 或者 vector<int> ilist2 = ilist; //拷貝初始化 ,"="
兩種方式等價(jià) , list2 初始化為list 的拷貝, list必須與list2 類型相同, 也就是同為int的vector類型, ilist2將具有和ilist相同的容量和元素
3.vector<int> list = {1,2,3.0,4,5,6,7};
vector<int> list {1,2,3.0,4,5,6,7};//列表中元素的拷貝 ilist 初始化為列表中元素的拷貝,列表中元素必須與ilist的元素類型相容, 本例中必須是與整數(shù)類型相容的類型,整形會(huì)直接拷貝,其他類型會(huì)進(jìn)行類型轉(zhuǎn)換。
4.vector<int> list3(list.begin()+2, list.end()-1); //比較常用
將points數(shù)組轉(zhuǎn)換成vector; 挺好用的; list3初始化為兩個(gè)迭代器指定范圍中元素的拷貝,范圍中的元素類型必須與list3 的元素類型相容, 在本例中ilist3被初始化為{3,4,5,6}。
注意:由于只要求范圍中的元素類型與待初始化的容器的元素類型相容,因此迭代器來自不同的容器是可能的,
例如,用一個(gè)double的list的范圍來初始化ilist3是可行的。另外由于構(gòu)造函數(shù)只是讀取范圍中的元素進(jìn)行拷貝,因此使用普通迭代器還是const迭代器來指出范圍并沒有區(qū)別。
這種初始化方法特別適合于獲取一個(gè)序列的子序列。
5.vector<int> ilist4(7); ilist4中將包含7個(gè)元素
默認(rèn)值初始化,ilist4中將包含7個(gè)元素,每個(gè)元素進(jìn)行缺省的值初始化, 對于int,也就是被賦值為0,因此ilist4被初始化為包含7個(gè)0。
當(dāng)程序運(yùn)行初期元素大致數(shù)量可預(yù)知,而元素的值需要?jiǎng)討B(tài)獲取的時(shí)候, 可采用這種初始化方式。
6.vector<int> ilist5(7,3);
指定值初始化,ilist5被初始化為包含7個(gè)值為3的int; 這個(gè)也比較常用。
std::vector使用總結(jié)
Vector
Vector描述的是一個(gè)動(dòng)態(tài)數(shù)組(dynamic array),并提供了相關(guān)操作和接口。
在使用Vector之前,需要引入頭文件#include<vector>
,在此頭文件中,類型vector是一個(gè)定義于namespace std內(nèi)的template:
template< class T, class Allocator = std::allocator<T> > class vector;
其中T可以是任意類型,Allocator用來定義內(nèi)存模型,默認(rèn)是C++標(biāo)準(zhǔn)庫提供的allocator
Vector的能力
Vector將元素復(fù)制到dynamic array內(nèi)部,是一種動(dòng)態(tài)的順序表結(jié)構(gòu)。Vector支持隨機(jī)訪問,可以以常量時(shí)間訪問元素,Vector支持隨機(jī)訪問迭代器,以及STL提供的任何算法(排序、查找等)。
但對于插入、刪除和移動(dòng)等操作,Vector效率較低(類似于數(shù)組的特性)。
大?。╯ize)和容量(capacity)
vector區(qū)別于一般數(shù)組的特性之一就是能夠動(dòng)態(tài)的“擴(kuò)容”,在容量能夠容納所有元素的前提下,提供基于pointer、reference、iterator的訪問,以及元素的操作等,因此,大小和容量的概念至關(guān)重要。
Vector的大?。╯ize)是指當(dāng)前元素所占用空間,而容量(capacity)則是指vector分配內(nèi)存預(yù)留大小,當(dāng)size超過capacity時(shí),vector會(huì)自動(dòng)進(jìn)行擴(kuò)容,重新分配內(nèi)存。
舉個(gè)例子——
vector<int> v; for (int i=0; i < 20; i++) { v.push_back(i); cout<<"i = "<<i << " size = " << v.size() << " capacity = " << v.capacity() << endl; }
可以看出,vector的大小是隨著插入元素不斷增加的,也就是說,size()的作用其實(shí)和sizeof的作用類似,但是由于vector都是reference語義的操作,sizeof一個(gè)vector對象,無法得到實(shí)際大小,所以vector提供了size成員函數(shù)。而capacity()則反映了vector的動(dòng)態(tài)擴(kuò)容機(jī)制。
vector容量的概念之所以重要,有兩個(gè)原因——
1.一旦內(nèi)存重新分配,vector相關(guān)元素的所有reference、pointer、iterator都會(huì)失效。這里的失效是指,原來的值需要更新到重新分配后的內(nèi)存地址。
2.內(nèi)存的重新分配需要一定時(shí)間。
reserve函數(shù)
通過reserve函數(shù)可以顯式的指定預(yù)留空間的大小,而避免vector反復(fù)的進(jìn)行內(nèi)存重新分配, 從而提高vector的使用效率。但reserve不能減小vector容量,比如,已存在vector.capacity = 100,使用reserve(80)不會(huì)有任何作用。
vector<int> v(100); cout<<v.size()<<" "<<v.capacity()<<endl; v.reserve(80); cout<<v.size()<<" "<<v.capacity()<<endl;
此外,還可以通過vector的構(gòu)造函數(shù)顯式的指定容量大小,比如:
vector<int> v(100);
使用這種方法的時(shí)候,vector會(huì)調(diào)用元素的default構(gòu)造函數(shù)進(jìn)行初始化,對于基礎(chǔ)類型,vector會(huì)進(jìn)行零值初始化(類似于Java的初始化機(jī)制),比如說——
class test{ static int count; public: test(){ count++; cout<<"test constructor function:"<<count<<endl; } }; int test::count = 0; int main(){ vector<test> v(10);// 調(diào)用10次test默認(rèn)構(gòu)造函數(shù) return 0; }
這種方法可能會(huì)頻繁調(diào)用構(gòu)造函數(shù),因此效率不如reverse的高。
shrink_to_fit函數(shù)
reverse函數(shù)無法縮減容量,但是很多時(shí)候,vector自動(dòng)擴(kuò)容機(jī)制分配的內(nèi)存往往是多余的,如果頻繁使用vector的話,這種內(nèi)存浪費(fèi)會(huì)相當(dāng)可觀。
C++11提供了縮減容量以符合當(dāng)前需求的函數(shù),shrink_to_fit函數(shù)。
該函數(shù)不具有強(qiáng)制力的要求,換言之,具體實(shí)現(xiàn)可能會(huì)因編譯器實(shí)現(xiàn)而不同。
但大多數(shù)情況下,縮減容量是有效的,比如——
vector<int> v; cout<<"capacity = "<<v.capacity()<<endl; v.reserve(10); cout<<"capacity = "<<v.capacity()<<endl; v.shrink_to_fit(); cout<<"capacity = "<<v.capacity()<<endl;
Vector操作
構(gòu)造函數(shù)、析構(gòu)函數(shù)
vector的構(gòu)造函數(shù)和析構(gòu)函數(shù)如下表所示——
序號(hào) | 操作 | 效果 |
---|---|---|
1 | vector<Elem> c | Default構(gòu)造函數(shù),產(chǎn)生一個(gè)vector,沒有任何元素 |
2 | vector<Elem>c(c2)vector<Elem>c=c2 | Copy構(gòu)造函數(shù),建立c2同型vector并成為c2的一份副本,該復(fù)制是深度復(fù)制 |
3 | vector<Elem>c(rv)vector<Elem>c=rv | rv是一個(gè)vector右值引用,那么這里的構(gòu)造函數(shù)是一個(gè)Move構(gòu)造函數(shù),建立一個(gè)新的vector,取右值內(nèi)容(C++11新特性) |
4 | vector<Elem>c(n) | 利用元素的默認(rèn)構(gòu)造函數(shù)生成一個(gè)大小為n(容量也為n)的vector |
5 | vector<Elem>c(n,elem) | 建立一個(gè)大小為n的vector,并初始化為elem |
6 | vector<Elem>c(beg,end) | 建立一個(gè)vector,并以迭代器所指向的區(qū)間[beg,end)作為元素值 |
7 | vector<Elem>c(initlist)vector<Elem>c=initlist | 建立一個(gè)vector,以初值列initlist元素為初值(C++11新特性) |
8 | c.~vector() | 銷毀所有元素,釋放內(nèi)存 |
其中3和7是C++11新特性,6是基于迭代器的,第一次見可能會(huì)比較陌生,但使用起來很方便,比如——
vector<int> v{ 1,2,3,4,5,6,7,8,9 };// initlist初始化 vector<int> v2(v.begin(), v.end());// 基于迭代器的初始化 vector<int> v3 = move(v2); // Move構(gòu)造函數(shù) for (const auto&elem : v3) // range-based for循環(huán) { cout << elem << " "; }
非更易型操作(Nonmodifying Operating)
STL中有非更易的概念,即不改變?nèi)萜鲀?nèi)元素.
序號(hào) | 操作 | 效果 |
---|---|---|
1 | c.empty() | 容器為空返回true,不為空返回false,相當(dāng)于size()==0 |
2 | c.size() | 返回當(dāng)前元素的個(gè)數(shù) |
3 | c.max_size() | 返回元素個(gè)數(shù)之最大可能量 |
4 | c.capacity() | 返回容器當(dāng)前最大容量 |
5 | c.reserve(n) | 如果容器不足,顯示擴(kuò)容,該操作會(huì)引起迭代器、指針、引用的失效,但并未改變元素的值,因此仍舊視為非更易型操作 |
6 | c.shrink_to_fit() | 降低容量,使得size()==capacity(),(C++11新特性) |
7 | c1==c2 | 對每個(gè)元素調(diào)用c1==c2,全部相等返回true |
8 | c1!=c2 | 只要有一個(gè)元素相等,返回true,相當(dāng)于!(c1==c2) |
9 | c1>c2,c1>=c2,c1<c2,c1<=c2 | 同上,依次類推 |
賦值操作(Assignment Operating)
賦值操作可能會(huì)引起元素的默認(rèn)構(gòu)造函數(shù)、復(fù)制構(gòu)造函數(shù)、賦值操作符等,詳細(xì)如下表——
序號(hào) | 操作 | 效果 |
---|---|---|
1 | c1=c2 | 把c2的全部元素賦值給c |
2 | c=rv | 將rvalue 右值引用以 move assignment的方式賦值給c(C++11新特性) |
3 | c = initlist | 將初值列initlist的所有元素賦值給c(C++11新特性) |
4 | c.assign(n,elem) | 復(fù)制n個(gè)elem,賦值給c |
5 | c.assign(beg,end) | 復(fù)制迭代器指向區(qū)間[beg,end)內(nèi)容,賦值給c |
6 | c.assign(initlist) | 將初值列initlist的所有元素賦值給c |
7 | c.swap(c2) | 置換c和c2的數(shù)據(jù) |
8 | swap(c1,c2) | 置換c1和c2的數(shù)據(jù) |
元素訪問(Element Access)
下表列出了所有訪問vector元素的方法,對于所有non-const元素,返回的都是元素的reference,這些操作中,只有at()
會(huì)檢查邊界,如果越界,拋出out_of_range
異常。——
序號(hào) | 操作 | 效果 |
---|---|---|
1 | c[index] | 返回索引index所指向的元素 |
2 | c.at(index) | 返回index所指向的元素(會(huì)進(jìn)行邊界檢查) |
3 | c.front() | 返回第一個(gè)元素(不會(huì)檢查第一元素是否存在) |
4 | c.back() | 返回最后一個(gè)元素(不會(huì)檢查最后一個(gè)元素是否存在) |
由于vector只提供了at函數(shù)的檢查元素,所以對于越界或者訪問空元素的情況,其結(jié)果是未定義的,視編譯器而異。
Visual Studio比較嚴(yán)格,將此行為定義為運(yùn)行時(shí)異常,而GCC則較為寬松,允許這一非法操作。
vector<int> v{ 1,2,3,4,5,6,7,8,9 }; v.clear(); cout << v.front() << endl;// Expression:front() called on empty vector. cout<< v[10]<<endl;// Expression:vector subscript out of range.
迭代器函數(shù)(Iterator Function)
vector支持隨機(jī)訪問(random access)迭代器,理論上STL提供的所有迭代器都能為其所用。
迭代器是STL提供操作容器的重要工具,熟練使用迭代器能夠?qū)TL各大容器的性能發(fā)揮到極致。
序號(hào) | 操作 | 效果 |
---|---|---|
1 | c.begin() | 返回一個(gè)randrom access iterator指向第一個(gè)元素 |
2 | c.end() | 返回一個(gè)random access iterator指向的之后一個(gè)元素 |
3 | c.cbegin() | 返回一個(gè)const random access iterator指向的第一個(gè)元素(C++11新特性) |
4 | c.cend() | 返回一個(gè)const random access iterator指向的最后一個(gè)元素(C++11新特性) |
5 | c.rbegin() | 返回一個(gè)反向迭代器(reverse iterator)指向的第一個(gè)元素 |
6 | c.rend() | 返回一個(gè)reverse iterator指向的最后一個(gè)元素 |
7 | c.crbegin() | 返回一個(gè)const reverse iterator指向的第一個(gè)元素(C++新特性) |
8 | c.crend() | 返回一個(gè)const reverse iterator指向的最后一個(gè)元素(C++11新特性) |
迭代器結(jié)合auto
以及range-based for循環(huán),能夠?qū)⒃氐脑L問以一種清新脫俗的方式展現(xiàn)出來——
vector<int> v{ 1,2,3,4,5,6,7,8,9 }; for (auto it = v.cbegin(); it != v.cend();++it) { cout << *it << " "; }
再比如說容器元素的逆序輸出——
vector<int> v{ 1,2,3,4,5,6,7,8,9 }; for (auto it = v.crbegin(); it != v.crend();++it) { cout << *it << " "; }
插入和移除(Inserting and Removing)
插入和移除無疑是最常用的操作,掌握了這些,基本上就可以使容器在自己的代碼中產(chǎn)生戰(zhàn)斗力——
序號(hào) | 操作 | 效果 |
---|---|---|
1 | c.push_back(elem) | 在vector末尾插入元素elem |
2 | c.pop_back() | 移除最后一個(gè)元素,但是不返回該元素 |
3 | c.insert(pos,elem) | 在iterator指向的pos位置的前方插入一個(gè)元素elem的副本,并返回新元素的位置(此時(shí)返回的是整型,而非iterator) |
4 | c.insert(pos,n,elem) | 在iterator指向的pos位置的前方插入n個(gè)元素的副本,并返回第一個(gè)新元素的位置 |
5 | c.insert(pos,beg,end) | 在iterator指向的pos位置的前方插入?yún)^(qū)間[beg,end)內(nèi)所有元素的副本,并返回第一個(gè)新元素的位置 |
6 | c.insert(pos,initlist) | 在iterator指向的pos位置的前方插入初始化列表所有元素的副本,并返回第一個(gè)元素的位置(C++11新特性) |
7 | c.emplace(pos,args…) | 在iterator指向的pos位置的前方插入一個(gè)以args為初值的元素,并返回新元素的位置(C++11新特性) |
8 | c.emplace_back(args…) | 在vector末尾附加一個(gè)args為初值的元素,不返回任何東西 |
9 | c.erase(pos) | 移除iterator位置pos上的元素,返回下一個(gè)元素的位置 |
10 | c.erase(beg,end) | 移除區(qū)間[beg,end)所指向的元素所有內(nèi)容,返回下一個(gè)元素的位置 |
11 | c.resize(num) | 將vector大小調(diào)整為num,若大小增大,新元素以默認(rèn)構(gòu)造函數(shù)或者零值進(jìn)行初始化 |
12 | c.resize(num,elem) | 將vector大小調(diào)整為num,若大小增大,新元素以elem進(jìn)行初始化 |
13 | c.clear() | 移除所有元素,容器清空 |
我們演示一個(gè)插入到刪除的過程——
vector<int>v; vector<int>v2{ -1,-2,-3,-4 }; cout << "Source data:"; for (int i = 0; i < 10; i++) { v.push_back(i); } for (const auto&elem : v) { cout << elem << " "; } cout << endl << "insert vector 2:"; v.insert(v.end(), v2.begin(), v2.end()); for (const auto&elem : v) { cout << elem << " "; } cout << endl << "remove vector 2:"; auto begin_it = v.begin(); while (*begin_it != *v2.begin()) { begin_it++; } v.erase(begin_it, v.end()); for (const auto&elem : v) { cout << elem << " "; }
異常處理
vector僅支持最低限度的邏輯差錯(cuò)檢查,Subscript操作符的安全版本at()是唯一一個(gè)被C++ standard認(rèn)可得以拋出異常的函數(shù),此外C++ standard 同時(shí)規(guī)定,只有一般標(biāo)準(zhǔn)異?;蛘弑挥脩糇远x的異常才可能發(fā)生,也就是說,vector的一些非法操作,在運(yùn)行時(shí)都不會(huì)拋出異常,但程序員需要對自己的非法操作負(fù)責(zé)。
1. 如果push_back安插元素時(shí)發(fā)生異常,函數(shù)不產(chǎn)生效用;
2. 如果元素remove/copy操作不拋出異常, 那么insert/emplace等要么成功,要么不拋出異常;
3. pop_back絕對不會(huì)拋出任何異常;
4. 如果元素remove/copy操作不拋出異常,erase也不會(huì)拋出異常;
5. swap和clear不會(huì)拋出異常;
6. 如果元素remove/copy操作不拋出異常,那么所有的操作不是成功,就是不產(chǎn)生任何效果,包括不拋出異常。
以上所有都基于“析構(gòu)函數(shù)不得拋出任何異常”的前提。但實(shí)際上,編譯器會(huì)做不同程度的優(yōu)化,比如熱心的VS,幾乎在所有vector可能出現(xiàn)的異常檢測上都做了處理,在C++ Standard未定義的部分做了諸多工作。
這些僅為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
關(guān)于C++靜態(tài)成員函數(shù)訪問非靜態(tài)成員變量的問題
靜態(tài)成員函數(shù)不能訪問非靜態(tài)成員,這是因?yàn)殪o態(tài)函數(shù)屬于類而不是屬于整個(gè)對象,靜態(tài)函數(shù)中的 member可能都沒有分配內(nèi)存。靜態(tài)成員函數(shù)沒有隱含的this自變量。所以,它就無法訪問自己類的非靜態(tài)成員2013-10-10C++的template模板中class與typename關(guān)鍵字的區(qū)別分析
這篇文章中我們來談一談C++的template模板中class與typename關(guān)鍵字的區(qū)別分析,同時(shí)會(huì)講到嵌套從屬名稱時(shí)的一些注意點(diǎn),需要的朋友可以參考下2016-06-06簡單分析C語言中指針數(shù)組與數(shù)組指針的區(qū)別
這篇文章主要介紹了C語言中指針數(shù)組與數(shù)組指針的區(qū)別,是C語言入門學(xué)習(xí)中的基礎(chǔ)知識(shí),需要的朋友可以參考下2015-11-11C語言詳解用char實(shí)現(xiàn)大小寫字母的轉(zhuǎn)換
這篇文章主要給大家介紹了關(guān)于C語言實(shí)現(xiàn)大小寫字母轉(zhuǎn)換的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-05-05C語言中fgetgrent()函數(shù)和fgetpwent()函數(shù)的用法對比
這篇文章主要介紹了C語言中fgetgrent()函數(shù)和fgetpwent()函數(shù)的用法對比,分別用于讀取組格式函數(shù)和讀取密碼格式,需要的朋友可以參考下2015-08-08