C++中std::vector的6種初始化方式
C++ std::vector的6種初始化
1.vector<int> list1; 默認初始化,最常用
此時,vector為空, size為0,表明容器中沒有元素,而且 capacity 也返回 0,意味著還沒有分配內存空間。 這種初始化方式適用于元素個數未知,需要在程序中動態(tài)添加的情況。
2.vector<int> list2(list); 或者 vector<int> ilist2 = ilist; //拷貝初始化 ,"="
兩種方式等價 , 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的元素類型相容, 本例中必須是與整數類型相容的類型,整形會直接拷貝,其他類型會進行類型轉換。
4.vector<int> list3(list.begin()+2, list.end()-1); //比較常用

將points數組轉換成vector; 挺好用的; list3初始化為兩個迭代器指定范圍中元素的拷貝,范圍中的元素類型必須與list3 的元素類型相容, 在本例中ilist3被初始化為{3,4,5,6}。
注意:由于只要求范圍中的元素類型與待初始化的容器的元素類型相容,因此迭代器來自不同的容器是可能的,
例如,用一個double的list的范圍來初始化ilist3是可行的。另外由于構造函數只是讀取范圍中的元素進行拷貝,因此使用普通迭代器還是const迭代器來指出范圍并沒有區(qū)別。
這種初始化方法特別適合于獲取一個序列的子序列。
5.vector<int> ilist4(7); ilist4中將包含7個元素
默認值初始化,ilist4中將包含7個元素,每個元素進行缺省的值初始化, 對于int,也就是被賦值為0,因此ilist4被初始化為包含7個0。
當程序運行初期元素大致數量可預知,而元素的值需要動態(tài)獲取的時候, 可采用這種初始化方式。
6.vector<int> ilist5(7,3);
指定值初始化,ilist5被初始化為包含7個值為3的int; 這個也比較常用。
std::vector使用總結
Vector
Vector描述的是一個動態(tài)數組(dynamic array),并提供了相關操作和接口。

在使用Vector之前,需要引入頭文件#include<vector>,在此頭文件中,類型vector是一個定義于namespace std內的template:
template<
class T,
class Allocator = std::allocator<T>
> class vector;其中T可以是任意類型,Allocator用來定義內存模型,默認是C++標準庫提供的allocator
Vector的能力
Vector將元素復制到dynamic array內部,是一種動態(tài)的順序表結構。Vector支持隨機訪問,可以以常量時間訪問元素,Vector支持隨機訪問迭代器,以及STL提供的任何算法(排序、查找等)。
但對于插入、刪除和移動等操作,Vector效率較低(類似于數組的特性)。
大?。╯ize)和容量(capacity)
vector區(qū)別于一般數組的特性之一就是能夠動態(tài)的“擴容”,在容量能夠容納所有元素的前提下,提供基于pointer、reference、iterator的訪問,以及元素的操作等,因此,大小和容量的概念至關重要?! ?/p>
Vector的大?。╯ize)是指當前元素所占用空間,而容量(capacity)則是指vector分配內存預留大小,當size超過capacity時,vector會自動進行擴容,重新分配內存。
舉個例子——
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()的作用其實和sizeof的作用類似,但是由于vector都是reference語義的操作,sizeof一個vector對象,無法得到實際大小,所以vector提供了size成員函數。而capacity()則反映了vector的動態(tài)擴容機制。
vector容量的概念之所以重要,有兩個原因——
1.一旦內存重新分配,vector相關元素的所有reference、pointer、iterator都會失效。這里的失效是指,原來的值需要更新到重新分配后的內存地址。
2.內存的重新分配需要一定時間。
reserve函數
通過reserve函數可以顯式的指定預留空間的大小,而避免vector反復的進行內存重新分配, 從而提高vector的使用效率。但reserve不能減小vector容量,比如,已存在vector.capacity = 100,使用reserve(80)不會有任何作用。
vector<int> v(100); cout<<v.size()<<" "<<v.capacity()<<endl; v.reserve(80); cout<<v.size()<<" "<<v.capacity()<<endl;

此外,還可以通過vector的構造函數顯式的指定容量大小,比如:
vector<int> v(100);
使用這種方法的時候,vector會調用元素的default構造函數進行初始化,對于基礎類型,vector會進行零值初始化(類似于Java的初始化機制),比如說——
class test{
static int count;
public:
test(){
count++;
cout<<"test constructor function:"<<count<<endl;
}
};
int test::count = 0;
int main(){
vector<test> v(10);// 調用10次test默認構造函數
return 0;
} 
這種方法可能會頻繁調用構造函數,因此效率不如reverse的高。
shrink_to_fit函數
reverse函數無法縮減容量,但是很多時候,vector自動擴容機制分配的內存往往是多余的,如果頻繁使用vector的話,這種內存浪費會相當可觀。
C++11提供了縮減容量以符合當前需求的函數,shrink_to_fit函數。
該函數不具有強制力的要求,換言之,具體實現可能會因編譯器實現而不同。
但大多數情況下,縮減容量是有效的,比如——
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操作
構造函數、析構函數
vector的構造函數和析構函數如下表所示——
| 序號 | 操作 | 效果 |
|---|---|---|
| 1 | vector<Elem> c | Default構造函數,產生一個vector,沒有任何元素 |
| 2 | vector<Elem>c(c2)vector<Elem>c=c2 | Copy構造函數,建立c2同型vector并成為c2的一份副本,該復制是深度復制 |
| 3 | vector<Elem>c(rv)vector<Elem>c=rv | rv是一個vector右值引用,那么這里的構造函數是一個Move構造函數,建立一個新的vector,取右值內容(C++11新特性) |
| 4 | vector<Elem>c(n) | 利用元素的默認構造函數生成一個大小為n(容量也為n)的vector |
| 5 | vector<Elem>c(n,elem) | 建立一個大小為n的vector,并初始化為elem |
| 6 | vector<Elem>c(beg,end) | 建立一個vector,并以迭代器所指向的區(qū)間[beg,end)作為元素值 |
| 7 | vector<Elem>c(initlist)vector<Elem>c=initlist | 建立一個vector,以初值列initlist元素為初值(C++11新特性) |
| 8 | c.~vector() | 銷毀所有元素,釋放內存 |
其中3和7是C++11新特性,6是基于迭代器的,第一次見可能會比較陌生,但使用起來很方便,比如——
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構造函數
for (const auto&elem : v3) // range-based for循環(huán)
{
cout << elem << " ";
}
非更易型操作(Nonmodifying Operating)
STL中有非更易的概念,即不改變容器內元素.
| 序號 | 操作 | 效果 |
|---|---|---|
| 1 | c.empty() | 容器為空返回true,不為空返回false,相當于size()==0 |
| 2 | c.size() | 返回當前元素的個數 |
| 3 | c.max_size() | 返回元素個數之最大可能量 |
| 4 | c.capacity() | 返回容器當前最大容量 |
| 5 | c.reserve(n) | 如果容器不足,顯示擴容,該操作會引起迭代器、指針、引用的失效,但并未改變元素的值,因此仍舊視為非更易型操作 |
| 6 | c.shrink_to_fit() | 降低容量,使得size()==capacity(),(C++11新特性) |
| 7 | c1==c2 | 對每個元素調用c1==c2,全部相等返回true |
| 8 | c1!=c2 | 只要有一個元素相等,返回true,相當于!(c1==c2) |
| 9 | c1>c2,c1>=c2,c1<c2,c1<=c2 | 同上,依次類推 |
賦值操作(Assignment Operating)
賦值操作可能會引起元素的默認構造函數、復制構造函數、賦值操作符等,詳細如下表——
| 序號 | 操作 | 效果 |
|---|---|---|
| 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) | 復制n個elem,賦值給c |
| 5 | c.assign(beg,end) | 復制迭代器指向區(qū)間[beg,end)內容,賦值給c |
| 6 | c.assign(initlist) | 將初值列initlist的所有元素賦值給c |
| 7 | c.swap(c2) | 置換c和c2的數據 |
| 8 | swap(c1,c2) | 置換c1和c2的數據 |
元素訪問(Element Access)
下表列出了所有訪問vector元素的方法,對于所有non-const元素,返回的都是元素的reference,這些操作中,只有at()會檢查邊界,如果越界,拋出out_of_range異常。——
| 序號 | 操作 | 效果 |
|---|---|---|
| 1 | c[index] | 返回索引index所指向的元素 |
| 2 | c.at(index) | 返回index所指向的元素(會進行邊界檢查) |
| 3 | c.front() | 返回第一個元素(不會檢查第一元素是否存在) |
| 4 | c.back() | 返回最后一個元素(不會檢查最后一個元素是否存在) |
由于vector只提供了at函數的檢查元素,所以對于越界或者訪問空元素的情況,其結果是未定義的,視編譯器而異。
Visual Studio比較嚴格,將此行為定義為運行時異常,而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.
迭代器函數(Iterator Function)
vector支持隨機訪問(random access)迭代器,理論上STL提供的所有迭代器都能為其所用。
迭代器是STL提供操作容器的重要工具,熟練使用迭代器能夠將STL各大容器的性能發(fā)揮到極致。
| 序號 | 操作 | 效果 |
|---|---|---|
| 1 | c.begin() | 返回一個randrom access iterator指向第一個元素 |
| 2 | c.end() | 返回一個random access iterator指向的之后一個元素 |
| 3 | c.cbegin() | 返回一個const random access iterator指向的第一個元素(C++11新特性) |
| 4 | c.cend() | 返回一個const random access iterator指向的最后一個元素(C++11新特性) |
| 5 | c.rbegin() | 返回一個反向迭代器(reverse iterator)指向的第一個元素 |
| 6 | c.rend() | 返回一個reverse iterator指向的最后一個元素 |
| 7 | c.crbegin() | 返回一個const reverse iterator指向的第一個元素(C++新特性) |
| 8 | c.crend() | 返回一個const reverse iterator指向的最后一個元素(C++11新特性) |
迭代器結合auto以及range-based for循環(huá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)
插入和移除無疑是最常用的操作,掌握了這些,基本上就可以使容器在自己的代碼中產生戰(zhàn)斗力——
| 序號 | 操作 | 效果 |
|---|---|---|
| 1 | c.push_back(elem) | 在vector末尾插入元素elem |
| 2 | c.pop_back() | 移除最后一個元素,但是不返回該元素 |
| 3 | c.insert(pos,elem) | 在iterator指向的pos位置的前方插入一個元素elem的副本,并返回新元素的位置(此時返回的是整型,而非iterator) |
| 4 | c.insert(pos,n,elem) | 在iterator指向的pos位置的前方插入n個元素的副本,并返回第一個新元素的位置 |
| 5 | c.insert(pos,beg,end) | 在iterator指向的pos位置的前方插入區(qū)間[beg,end)內所有元素的副本,并返回第一個新元素的位置 |
| 6 | c.insert(pos,initlist) | 在iterator指向的pos位置的前方插入初始化列表所有元素的副本,并返回第一個元素的位置(C++11新特性) |
| 7 | c.emplace(pos,args…) | 在iterator指向的pos位置的前方插入一個以args為初值的元素,并返回新元素的位置(C++11新特性) |
| 8 | c.emplace_back(args…) | 在vector末尾附加一個args為初值的元素,不返回任何東西 |
| 9 | c.erase(pos) | 移除iterator位置pos上的元素,返回下一個元素的位置 |
| 10 | c.erase(beg,end) | 移除區(qū)間[beg,end)所指向的元素所有內容,返回下一個元素的位置 |
| 11 | c.resize(num) | 將vector大小調整為num,若大小增大,新元素以默認構造函數或者零值進行初始化 |
| 12 | c.resize(num,elem) | 將vector大小調整為num,若大小增大,新元素以elem進行初始化 |
| 13 | c.clear() | 移除所有元素,容器清空 |
我們演示一個插入到刪除的過程——
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僅支持最低限度的邏輯差錯檢查,Subscript操作符的安全版本at()是唯一一個被C++ standard認可得以拋出異常的函數,此外C++ standard 同時規(guī)定,只有一般標準異常或者被用戶自定義的異常才可能發(fā)生,也就是說,vector的一些非法操作,在運行時都不會拋出異常,但程序員需要對自己的非法操作負責。
1. 如果push_back安插元素時發(fā)生異常,函數不產生效用;
2. 如果元素remove/copy操作不拋出異常, 那么insert/emplace等要么成功,要么不拋出異常;
3. pop_back絕對不會拋出任何異常;
4. 如果元素remove/copy操作不拋出異常,erase也不會拋出異常;
5. swap和clear不會拋出異常;
6. 如果元素remove/copy操作不拋出異常,那么所有的操作不是成功,就是不產生任何效果,包括不拋出異常。
以上所有都基于“析構函數不得拋出任何異常”的前提。但實際上,編譯器會做不同程度的優(yōu)化,比如熱心的VS,幾乎在所有vector可能出現的異常檢測上都做了處理,在C++ Standard未定義的部分做了諸多工作。
這些僅為個人經驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關文章
關于C++靜態(tài)成員函數訪問非靜態(tài)成員變量的問題
靜態(tài)成員函數不能訪問非靜態(tài)成員,這是因為靜態(tài)函數屬于類而不是屬于整個對象,靜態(tài)函數中的 member可能都沒有分配內存。靜態(tài)成員函數沒有隱含的this自變量。所以,它就無法訪問自己類的非靜態(tài)成員2013-10-10
C++的template模板中class與typename關鍵字的區(qū)別分析
這篇文章中我們來談一談C++的template模板中class與typename關鍵字的區(qū)別分析,同時會講到嵌套從屬名稱時的一些注意點,需要的朋友可以參考下2016-06-06
C語言中fgetgrent()函數和fgetpwent()函數的用法對比
這篇文章主要介紹了C語言中fgetgrent()函數和fgetpwent()函數的用法對比,分別用于讀取組格式函數和讀取密碼格式,需要的朋友可以參考下2015-08-08

