C++ Vector 使用总结
注意,在使用前要包含vector對應的頭文件:
#include
vector是同一種類型的對 象的集合,每個對象都有一個對應的整數索引值。和string對象一樣,標準庫負責管理存儲元素的相關內存。我們把vector稱為容器,是因為它可以包 含其他對象。一個容器中的所有對象都必須是同一種類型的。我們將在第9章更詳細地介紹容器。
使用vector之前,必須包含相應的頭文件。本書給出的例子,都是假設已作了相應的using聲明:
#include
using std::vector;
vector 是一個類模板(class template)。模板允許程序員編寫單個類或函數定義,這個類和函數定義可用于不同的數據類型上。因此,我們可以定義保存string對象的 vector,或保存int值的vector,又或是保存自定義的類類型對象(如Sales_item對象)的vector。將在第16章介紹如何定義程 序員自己的類模板。幸運的是,使用類模板時只需要簡單了解類模板是如何定義的就可以了。
聲明從類模板產生的某種類型的對象,需要提供附加信息,信息的種類取決于模板。以vector為例,必須說明vector保存何種對象的類型,通過將類型放在類模板名稱后面的尖括號中來指定類型:
vector?ivec;?????????????????// ivec holds objects of type int
vector?Sales_vec;??// holds Sales_items
和其 他變量定義一樣,定義vector對象要指定類型和一個變量的列表。上面的第一個定義,類型是vector,該類型即是含有若干 int類型對象的vector,變量名為ivec。第二個定義的變量名是Sales_vec,它所保存的元素是Sales_item類型的對象。
vector不是一種數據類型,而只是一個類模板,可用來定義任意多種數據類型。vector類型的每一種都指定了其保存元素的類型。因此,vector和vector?都是數據類型。
3.3.1??vector對象的定義和初始化
vector類定義了好幾種構造函數(2.3.3節),用來定義和初始化vector對象。表3-4列出了這些構造函數:
表3-4??幾種初始化vector對象的方式
| vector??v1; | vector保存類型為T的對象。默認構造函數v1為空。 |
| vector?v2(v1); | v2是v1的一個副本。 |
| vector?v3(n, i); | v3包含n個值為i的元素。 |
| vector?v4(n); | v4含有值初始化的元素的n個副本。 |
1. 創建確定個數的元素
若要創建非空的vector對象,必須給出初始化元素的值。當把一個vector對象復制到另一個vector對象時,新復制的vector中每一個元素都初始化為原vector中相應元素的副本。但這兩個vector對象必須保存同一種元素類型:
vector?ivec1;????????????????// ivec1 holds objects of type int
vector?ivec2(ivec1);???????// ok: copy elements of ivec1 into ivec2
vector?svec(ivec1);????// error: svec holds strings, not ints
可以用元素個數和元素值對vector對象進行初始化。構造函數用元素個數來決定vector對象保存元素的個數,元素值指定每個元素的初始值:
vector?ivec4(10, -1);?????// 10 elements, each initialized to -1
vector?svec(10, "hi!"); // 10 strings, each initialized to "hi!"
關鍵概念:vector對象動態增長???????????????????????????????????????????????????????
vector對象(以及其他標準庫容器對象)的重要屬性就在于可以在運行時高效地添加元素。因為vector增長的效率高,在元素值已知的情況下,最好是動態地添加元素。
正如 第4章將介紹的,這種增長方式不同于C語言中的內置數據類型,也不同于大多數其他編程語言的數據類型。特別地,如果讀者習慣了C或Java的風格,由于 vector元素連續存儲,可能希望最好是預先分配合適的空間。但事實上,為了達到連續性,C++的做法恰好相反,具體原因將在第9章探討。
雖然可以對給定元素個數的vector對象預先分配內存,但更有效的方法是先初始化一個空vector對象,然后再動態地增加元素(我們隨后將學習如何進行這樣的操作)。
?
2. 值初始化
如果沒有給出元素的初始化式,那么標準庫將提供一個值初始化的(value initialized)元素初始化式。這個由庫生成的初始值用于初始化容器中的每個元素。而元素初始化式的值取決于存儲在vector中元素的數據類型。
如果vector保存內置類型(如int類型)的元素,那么標準庫將用0值創建元素初始化值:
vector?fvec(10); // 10 elements, each initialized to 0
如果向量保存類類型(如string)的元素,標準庫將用該類型的默認構造函數創建元素初始值:
vector?svec(10); // 10 elements, each an empty string
第12章將介紹一些有自定義構造函數但沒有默認構造函數的類,在初始化這種類型的Vector對象時,程序員就不能僅提供元素個數,還需要提供元素初始值。
還有第三種可能性:元素類型可能是沒有定義任何構造函數的類類型。這種情況下,標準庫仍產生一個帶初始值的對象,這個對象的每個成員進行了值初始化。
習題???????????????????????????????????????????????????????????
習題3.11??下面哪些vector定義不正確?
???(a) vector< vector?> ivec;
???(b) vector?svec = ivec ;
???(c) vector?svec(10,”null”);
習題3.12??下列每個vector對象中元素個數是多少?各元素的值是什么?
???(a) vector?ivec1;
???(b) vector?ivec2(10);
???(c) vector?ivec3(10,42);
???(d) vector?svec1;
???(e) vector?svec2(10);
???(f) vector?svec3(10,”hello”);
3.3.2??vector的操作
vector標準庫提供許多類似于string對象的操作,表3-5列出了幾種最重要的vector操作。
表3-5??vector操作
| v.empty() | 如果v為空,則返回true,否則返回false。 |
| v.size() | 返回v中元素的個數。 |
| v.push_back(t) | 在v的末尾增加一個值為t的元素。 |
| v[n] | 返回v中位置為n的元素。 |
| v1 = v2 | 把v1的元素替換為v2中元素的副本。 |
| v1 == v2 | 如果v1與v2相等,則返回true。 |
| !=, <, <=, >, >= | 保持這些操作符慣有的含義。 |
1.?vector對象的size
empty和size操作類似于string類型的相關操作(3.2.3節)。成員函數size返回相應vector類定義的size_type的值。
使用size_type類型時,必須指出該類型是在哪里定義的。vector類型總是包括vector的元素類型:
vector::size_type????????// ok
vector::size_type??????????// error
2. 向vector添加元素
push_back()操作接受一個元素值,并將它作為一個新的元素添加到vector對象的后面,也就是“插入(push)”到vector對象的“后面(back)”:
// read words from the standard input and store them as elements in a vector
string word;
vector?text;????????// empty vector
while (cin >> word) {
????text.push_back(word);??// append word to text
}
該循環從標準輸入讀取一系列string對象,逐一追加到vector對象的后面。首先定義一個空的vector對象text。每循環一次就添加一個新元素到vector對象,并將從輸入讀取的word值賦予該元素。當循環結束時,text就包含了所有讀入的元素。
3.?vector的下標操作
vector中的對象是沒有命名的,可以按vector中對象的位置來訪問它們。通常使用下標操作符來獲取元素。vector的下標操作類似于string類型的下標操作(3.2.3節)。
vector的下標操作符接受一個值,并返回vector中該對應位置的元素。vector元素的位置從0開始。下例使用for循環把vector中的每個元素值都重置為0:
// reset the elements in the vector to zero
for (vector::size_type ix = 0; ix != ivec.size(); ++ix)
????ivec[ix] = 0;
和string類型的下標操作符一樣,vector下標操作的結果為左值,因此可以像循環體中所做的那樣實現寫入。另外,和string對象的下標操作類似,這里用size_type類型作為vector下標的類型。
在上例中,即使ivec為空,for循環也會正確執行。ivec為空則調用size返回0,并且for中的測試比較ix和0。第一次循環時,由于ix本身就是0,則條件測試失敗,for循環體一次也不執行。
關鍵概念:安全的泛型編程???????????????????????????????????????????????????????
習慣 于C或Java編程的C++程序員可能會覺得難以理解,for循環的判斷條件用!=而不是用<來測試vector下標值是否越界。C程序員難以理解 的還有,上例中沒有在for循環之前就調用size成員函數并保存其返回的值,而是在for語句頭中調用size成員函數。
C++程序員習慣于優先選用!=而不是<來編寫循環判斷條件。在上例中,選用或不用某種操作符并沒有特別的取舍理由。學習完本書第二部分的泛型編程后,你將會明白這個習慣的合理性。
調用 size成員函數而不保存它返回的值,在這個例子中同樣不是必需的,但這反映了一個良好的編程習慣。在C++中,有些數據結構(如vector)可以動態 增長。上例中循環僅需要讀取元素,而不需要增加新的元素。但是,循環可以容易地增加新元素,如果確實增加了新元素的話,那么測試已保存的size值作為循 環的結束條件就會有問題,因為沒有將新加入的元素計算在內。所以我們傾向于在每次循環中測試size的當前值,而不是在進入循環時,存儲size值的副 本。
我們將在第7章學習到,C++中有些函數可以聲明為內聯(inline)函數。編譯器遇到內聯函數時就會直接擴展相應代碼,而不是進行實際的函數調用。像size這樣的小庫函數幾乎都定義為內聯函數,所以每次循環過程中調用它的運行時代價是比較小的。
?
4. 下標操作不添加元素
初學C++的程序員可能會認為vector的下標操作可以添加元素,其實不然:
vector?ivec;???// empty vector
for (vector::size_type ix = 0; ix != 10; ++ix)
?????ivec[ix] = ix; // disaster: ivec has no elements
上述程序試圖在ivec中插入10個新元素,元素值依次為0到9的整數。但是,這里ivec是空的vector對象,而且下標只能用于獲取已存在的元素。
這個循環的正確寫法應該是:
for (vector::size_type ix = 0; ix != 10; ++ix)
?????ivec.push_back(ix);??// ok: adds new element with value ix
| ? | |
| ? | ? |
必須是已存在的元素才能用下標操作符進行索引。通過下標操作進行賦值時,不會添加任何元素。
警告:僅能對確知已存在的元素進行下標操作???????????????????????????????????????????
對于下標操作符([]操作符)的使用有一點非常重要,就是僅能提取確實已存在的元素,例如:
vector?ivec;??????// empty vector
cout << ivec[0];????????// Error: ivec has no elements!
vector?ivec2(10); // vector with 10 elements
cout << ivec[10];??????// Error: ivec has elements 0...9
試圖獲取不存在的元素必然產生運行時錯誤。和大多數同類錯誤一樣,不能確保執行過程可以捕捉到這類錯誤,運行程序的結果是不確定的。由于取不存在的元素的結果是未定義的,因而不同的實現會導致不同的結果,但程序運行時幾乎肯定會以某種有趣的方式失敗。
本警告適用于任何使用下標操作的時候,如string類型的下標操作,以及將要簡要介紹的內置數組的下標操作。
總結
以上是生活随笔為你收集整理的C++ Vector 使用总结的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 关于static变量,请选择下面所有说法
- 下一篇: C++中的vector使用范例-