vector是STL中的最常見的容器,它是一種順序容器,支持隨機訪問。簡單的說vector就是一個能存放任意類型的動態數組,只不過數組是靜態的分配空間,一旦分配了空間大小就不能在改變了,但是vector是動態分配內存,它隨著元素的不斷插入,會按照自身的一套機制不斷擴充自己的容量。
vector的擴充機制:按照容器現在容量的一倍進行增長。vector容器分配的是一塊連續的內存空間,每次容器的增長,並不是在原油連續的內存空間後在進行簡單的疊加,而是重新申請一塊更大的新內存,並把現有容器內的元素逐個復制過去,然後銷毀舊的內存。這時原有指向舊內存空間的迭代器已經失效,所以當操作容器時,迭代器要及時更新。
vector是一個類模板。使用類模板可以編寫一個類型定義或函數定義,而用於多個不同的數據類型。所以vector不是一種數據類型,而是一個類模板,可以用來定義任意多種數據類型,vector類型的每一種都制定了其保存元素的類型。
當我們要使用vector的時候,我們必須在頭文件中加入 #include
vector是屬於std命名域的,需要通過命名限定,
using std::vector; vector或者連在一起,使用全名vInts;
std::vector也可以使用全局的命名域方式:vInts;
using namespace std;
vector的初始化
#include#include using namespace std; int main() { vector vec1; //創建一個空的vector vector vec2(vec1); //復制vec1 vector vec3(10); //創建一個vector,含有n個數據,數據均以缺省構造產生 vector vec4(10, 1);//創建一個含有10個1的vector }
vector的操作
vec.assign(beg,end); vec.assign(n,elem); // 將[beg; end)區間中的數據賦值給c。將n個elem的拷貝賦值給c。 vec.at(idx) // 傳回索引idx所指的數據,如果idx越界,拋出out_of_range。 vec.back() // 傳回最後一個數據,不檢查這個數據是否存在。 vec.begin() // 傳回迭代器中的第一個數據地址。 vec.capacity() // 返回容器當前已分配的容量。 vec.clear() // 移除容器中所有數據。 vec.empty() // 判斷容器是否為空。 vec.end() // 指向迭代器中末端元素的下一個,指向一個不存在元素。 vec.erase(pos) // 刪除pos位置的數據,傳回下一個數據的位置。 vec.erase(beg,end) //刪除[beg,end)區間的數據,傳回下一個數據的位置。 vec.front() // 傳回第一個數據。 get_allocator // 使用構造函數返回一個拷貝。 vec.insert(pos,elem) // 在pos位置插入一個elem拷貝,傳回新數據位置。 vec.insert(pos,n,elem) // 在pos位置插入n個elem數據。無返回值。 vec.insert(pos,beg,end) // 在pos位置插入在[beg,end)區間的數據。無返回值。 vec.max_size() // 返回容器中最大數據的數量。 vec.pop_back() // 刪除最後一個數據。 vec.push_back(elem) // 在尾部加入一個數據。 vec.rbegin() // 傳回一個逆向隊列的第一個數據。 vec.rend() // 傳回一個逆向隊列的最後一個數據的下一個位置。 vec.resize(num) // 重新指定隊列的長度。 vec.reserve() // 保留適當的容量。 vec.size() // 返回容器中實際數據的個數。 vec1.swap(vec2) swap(vec1,vec2) // 將c1和c2元素互換。同上操作。下面是部分成員函數的操作
#include#include //必須包含頭文件 using namespace std; int main() { //幾種vector聲明 vector v1; //定義空的vector vector v2(10); //產生大小為10的vector vector v3(10,-1); //產生大小為10,並且每個元素都是-1的vector vector v4(v3); //用一個vector產生一個vecotr int arr[5]={1,2,3,4,5}; vector v5(arr,&arr[5]); //以區間[beg;end)做為初值的vector cout<<"當前元素數量"< ::iterator ita; //聲明一個迭代器 int i=0; for(ita=v1.begin(), i=0;ita != v1.end();i++,ita++)//v1.begin()指向v1的第一個元素,v1.end()指向最後元素的下一位置 { cout<<"v1中的"<::reverse_iterator ita; v2=v1; //將v1的元素全部拷到v2 for(ita=v2.begin(),i=0;ita != v2.end();i++,ita++) { cout<<"v2中的"<::iterator pos=v1.begin(); v1.insert(pos,11); //v1.insert(pos,4,55); //如果直接用就是錯的,因為迭代器失效了 //v1.insert(pos,arr,&arr[5]); for(ita=v1.begin(),i=0;ita != v1.end();i++,ita++) { cout<<"v1中的"< 以下內容來源於他人
內存管理與效率
1》使用reserve()函數提前設定容量大小,避免多次容量擴充操作導致效率低下。
關於STL容器,最令人稱贊的特性之一就是是只要不超過它們的最大大小,它們就可以自動增長到足以容納你放進去的數據。(要知道這個最大值,只要調用名叫max_size的成員函數。)對於vector和string,如果需要更多空間,就以類似realloc的思想來增長大小。vector容器支持隨機訪問,因此為了提高效率,它內部使用動態數組的方式實現的。在通過 reserve() 來申請特定大小的時候總是按指數邊界來增大其內部緩沖區。當進行insert或push_back等增加元素的操作時,如果此時動態數組的內存不夠用,就要動態的重新分配當前大小的1.5~2倍的新內存區,再把原數組的內容復制過去。所以,在一般情況下,其訪問速度同一般數組,只有在重新分配發生時,其性能才會下降。正如上面的代碼告訴你的那樣。而進行pop_back操作時,capacity並不會因為vector容器裡的元素減少而有所下降,還會維持操作之前的大小。對於vector容器來說,如果有大量的數據需要進行push_back,應當使用reserve()函數提前設定其容量大小,否則會出現許多次容量擴充操作,導致效率低下。
reserve成員函數允許你最小化必須進行的重新分配的次數,因而可以避免真分配的開銷和迭代器/指針/引用失效。但在我解釋reserve為什麼可以那麼做之前,讓我簡要介紹有時候令人困惑的四個相關成員函數。在標准容器中,只有vector和string提供了所有這些函數。
(1) size()告訴你容器中有多少元素。它沒有告訴你容器為它容納的元素分配了多少內存。 (2) capacity()告訴你容器在它已經分配的內存中可以容納多少元素。那是容器在那塊內存中總共可以容納多少元素,而不是還可以容納多少元素。如果你想知道一個vector或string中有多少沒有被占用的內存,你必須從capacity()中減去size()。如果size和capacity返回同樣的值,容器中就沒有剩余空間了,而下一次插入(通過insert或push_back等)會引發上面的重新分配步驟。 (3) resize(Container::size_type n)強制把容器改為容納n個元素。調用resize之後,size將會返回n。如果n小於當前大小,容器尾部的元素會被銷毀。如果n大於當前大小,新默認構造的元素會添加到容器尾部。如果n大於當前容量,在元素加入之前會發生重新分配。 (4) reserve(Container::size_type n)強制容器把它的容量改為至少n,提供的n不小於當前大小。這一般強迫進行一次重新分配,因為容量需要增加。(如果n小於當前容量,vector忽略它,這個調用什麼都不做,string可能把它的容量減少為size()和n中大的數,但string的大小沒有改變。在我的經驗中,使用reserve來從一個string中修整多余容量一般不如使用“交換技巧”,那是條款17的主題。)
這個簡介表示了只要有元素需要插入而且容器的容量不足時就會發生重新分配(包括它們維護的原始內存分配和回收,對象的拷貝和析構和迭代器、指針和引用的失效)。所以,避免重新分配的關鍵是使用reserve盡快把容器的容量設置為足夠大,最好在容器被構造之後立刻進行。
例如,假定你想建立一個容納1-1000值的vector
。沒有使用reserve,你可以像這樣來做: vector
v; for (int i = 1; i <= 1000; ++i) v.push_back(i); 在大多數STL實現中,這段代碼在循環過程中將會導致2到10次重新分配。(10這個數沒什麼奇怪的。記住vector在重新分配發生時一般把容量翻倍,而1000約等於210。) 把代碼改為使用reserve,我們得到這個:
vector
v; v.reserve(1000); for (int i = 1; i <= 1000; ++i) v.push_back(i); 這在循環中不會發生重新分配。 在大小和容量之間的關系讓我們可以預言什麼時候插入將引起vector或string執行重新分配,而且,可以預言什麼時候插入會使指向容器中的迭代器、指針和引用失效。例如,給出這段代碼,
string s; ... if (s.size() < s.capacity()) { s.push_back('x'); } push_back的調用不會使指向這個string中的迭代器、指針或引用失效,因為string的容量保證大於它的大小。如果不是執行push_back,代碼在string的任意位置進行一個insert,我們仍然可以保證在插入期間沒有發生重新分配,但是,與伴隨string插入時迭代器失效的一般規則一致,所有從插入位置到string結尾的迭代器/指針/引用將失效。
回到本條款的主旨,通常有兩情況使用reserve來避免不必要的重新分配。第一個可用的情況是當你確切或者大約知道有多少元素將最後出現在容器中。那樣的話,就像上面的vector代碼,你只是提前reserve適當數量的空間。第二種情況是保留你可能需要的最大的空間,然後,一旦你添加完全部數據,修整掉任何多余的容量。
2》使用“交換技巧”來修整vector過剩空間/內存
有一種方法來把它從曾經最大的容量減少到它現在需要的容量。這樣減少容量的方法常常被稱為“收縮到合適(shrink to fit)”。該方法只需一條語句:vector
(ivec).swap(ivec); 表達式vector (ivec)建立一個臨時vector,它是ivec的一份拷貝:vector的拷貝構造函數做了這個工作。但是,vector的拷貝構造函數只分配拷貝的元素需要的內存,所以這個臨時vector沒有多余的容量。然後我們讓臨時vector和ivec交換數據,這時我們完成了,ivec只有臨時變量的修整過的容量,而這個臨時變量則持有了曾經在ivec中的沒用到的過剩容量。在這裡(這個語句結尾),臨時vector被銷毀,因此釋放了以前ivec使用的內存,收縮到合適。 3》用swap方法強行釋放STL Vector所占內存
template < class T> void ClearVector( vector
& v ) { vector vtTemp; vtTemp.swap( v ); } 如 vector v ; nums.push_back(1); nums.push_back(3); nums.push_back(2); nums.push_back(4); vector ().swap(v); /* 或者v.swap(vector
()); */ /*或者{ std::vector
tmp = v; v.swap(tmp); }; //加大括號{ }是讓tmp退出{ }時自動析構*/