一、概述
vector 稱為容器模板類,是同一種類型的對象的集合,每個對象都有一個對應的整數索引值。vector 不是一種數據類型,而只是一個類模板,可用來定義任意多種數據類型。vector 類型的每一種都指定了其保存元素的類型,如vector<int> 和vector<string> 都是數據類型。vector是一個能夠存放任意類型的動態數組,能夠增加和壓縮數據。
#include <vector>
using namespace std;
二、vector定義與初始化
vector<typeName> vt(length);
//length可以是整型常量,也可以是整型變量,也可以為空;當為空時,表示創建一個0長度的vector。
vector <T> vt1; //vector 保存類型為 T 的對象。默認構造函數 vt1 為空。
vector <T> vt2(vt1); //vt2 是 vt1 的一個副本,深拷貝。
vector <T> vt3(n, i); //vt3 包含有 n 個 i 元素。
vector <T> vt4(n); //vt4 包含有 n 個元素。
vector <T> vt5(vt4.begin(), vt4.begin()+3); //將向量 vt4 的 0 - 2 索引的值初始化為vt5;vt4.end() 表示結束位置。
//使用數組初始化向量
int n[] = {1, 2, 3, 4, 5};
vector <int> vt5(n, n+5); //將數組的前5個元素作為向量 vt5 的初值。
vector <int> vt6(&n[1], &n[4]); //將 n[1] 到 n[4] 范圍的元素作為向量 vt6 的初始值。
1 #include <iostream> 2 #include <vector> 3 using namespace std; 4 5 template <typename T> 6 void Showelements(vector <T>vec, int n); 7 8 int main(void) 9 { 10 //雖然可以對給定元素個數的 vector 對象預先分配內存, 11 //但更有效的方法是先初始化一個空 vector 對象, 12 //然后再動態地增加元素 13 14 //定義,且長度為 0 的空vector對象 15 vector <int> vt1; 16 17 //定義,初始化每一個元素為0 18 vector <int> vt2(10); 19 Showelements(vt2, 10); 20 21 //定義,初始化每一個元素為單字符 ‘*’ 22 vector <char> vt3(10, '*'); 23 Showelements(vt3, 10); 24 25 vector <string> vt4(10, "hi!"); 26 Showelements(vt4, 10); 27 28 //定義,深拷貝初始化 29 vector <int> vt5(vt2); 30 Showelements(vt5, 10); 31 32 } 33 34 template <typename T> 35 void Showelements(vector <T> vec, int n) 36 { 37 cout << "-----------------------" << endl; 38 int i = 0; 39 for (i = 0; i < n; i++) 40 { 41 cout << "\t#" << i << ": " << vec[i] << endl; 42 } 43 return; 44 }
二、vector對象作為參數
常用的vector容器作為參數時,有三種傳參數方式,分別如下:
> function1(vector <T> vec); //傳值
> function2(vector <T> & vec); //傳引用
> function3(vector <T> * vec); //傳指針
// 分別可對應const形式
三種方式分別對應的調用方式:
function1(vec); //傳值
function2(vec); //傳引用
function3(&vec); //傳地址
三種方式的效果:
發生拷貝構造;
不發生拷貝構造;
不發生拷貝構造;
1 #include <iostream> 2 #include <vector> 3 4 using namespace std; 5 6 void function1(std::vector<std::vector<int> > vec) 7 { 8 cout<<"-----------------------------------------"<<endl; 9 //打印vec的地址 10 cout<<"function1.&vec:"<<&vec<<endl; 11 //打印vec[i]的地址(即第一層vector的地址) 12 cout<<"function1.&vec[i]:"<<endl; 13 for(int i=0;i<2;i++) 14 cout<<&vec[i]<<endl; 15 //打印vec的各元素地址 16 cout<<"function1.&vec[i][j]:"<<endl; 17 for(int i=0;i<2;i++) 18 { 19 for(int j=0;j<3;j++) 20 cout<<&vec[i][j]<<" "; 21 cout<<endl; 22 } 23 cout<<"---------------------------"<<endl; 24 //打印vec的各元素值 25 cout<<"function1.vec[i][j]:"<<endl; 26 for(int i=0;i<2;i++) 27 { 28 for(int j=0;j<3;j++) 29 cout<<vec[i][j]<<" "; 30 cout<<endl; 31 } 32 } 33 void function2(std::vector<std::vector<int> >& vec) 34 { 35 cout<<"-----------------------------------------"<<endl; 36 //打印vec的地址 37 cout<<"function2.&vec:"<<&vec<<endl; 38 //打印vec[i]的地址(即第一層vector的地址) 39 cout<<"function2.&vec[i]:"<<endl; 40 for(int i=0;i<2;i++) 41 cout<<&vec[i]<<endl; 42 //打印vec的各元素地址 43 cout<<"function2.&vec[i][j]:"<<endl; 44 for(int i=0;i<2;i++) 45 { 46 for(int j=0;j<3;j++) 47 cout<<&vec[i][j]<<" "; 48 cout<<endl; 49 } 50 cout<<"---------------------------"<<endl; 51 //打印vec的各元素值 52 cout<<"function2.vec[i][j]:"<<endl; 53 for(int i=0;i<2;i++) 54 { 55 for(int j=0;j<3;j++) 56 cout<<vec[i][j]<<" "; 57 cout<<endl; 58 } 59 60 } 61 void function3(std::vector<std::vector<int> > *vec) 62 { 63 cout<<"-----------------------------------------"<<endl; 64 //打印vec的地址 65 cout<<"function3.&vec:"<<vec<<endl; 66 //打印vec[i]的地址(即第一層vector的地址) 67 cout<<"function3.&vec[i]:"<<endl; 68 for(int i=0;i<2;i++) 69 cout<<&(*vec)[i]<<endl; 70 //打印vec的各元素地址 71 cout<<"function3.&vec[i][j]:"<<endl; 72 for(int i=0;i<2;i++) 73 { 74 for(int j=0;j<3;j++) 75 cout<<&(*vec)[i][j]<<" "; 76 cout<<endl; 77 } 78 cout<<"---------------------------"<<endl; 79 //打印vec的各元素值 80 cout<<"function3.vec[i][j]:"<<endl; 81 for(int i=0;i<2;i++) 82 { 83 for(int j=0;j<3;j++) 84 cout<<(*vec)[i][j]<<" "; 85 cout<<endl; 86 } 87 } 88 89 int main() 90 { 91 //創建2*3的vector容器v,初始值均初始化為0 1 2 1 2 3 92 std::vector<std::vector<int> > v(2,std::vector<int>(3,0)); 93 for(int i=0;i<2;i++) 94 { 95 for(int j=0;j<3;j++) 96 v[i][j]=i+j; 97 } 98 99 //打印v的地址 100 cout<<"&v:"<<&v<<endl; 101 //打印v[i]的地址(即第一層vector的地址) 102 cout<<"&v[i]:"<<endl; 103 for(int i=0;i<2;i++) 104 cout<<&v[i]<<endl; 105 //打印v的各元素地址 106 cout<<"&v[i][j]:"<<endl; 107 for(int i=0;i<2;i++) 108 { 109 for(int j=0;j<3;j++) 110 cout<<&v[i][j]<<" "; 111 cout<<endl; 112 } 113 114 cout<<"---------------------------"<<endl; 115 //打印v的各元素值 116 cout<<"v[i][j]:"<<endl; 117 for(int i=0;i<2;i++) 118 { 119 for(int j=0;j<3;j++) 120 cout<<v[i][j]<<" "; 121 cout<<endl; 122 } 123 124 function1(v); 125 function2(v); 126 function3(&v); 127 128 return 0; 129 }
三、vector 容器函數
vector(向量): C++中的一種數據結構,確切的說是一個類.它相當於一個動態的數組,當程序員無法知道自己需要的數組的規模多大時,用其來解決問題可以達到最大節約空間的目的.
它是一個多功能的,能夠操作多種數據結構和算法的模板類和函數庫。vector之所以被認為是一個容器,是因為它能夠像容器一樣存放各種類型的對象,簡單地說,vector是一個能夠存放任意類型的動態數組,能夠增加和壓縮數據。使用vector庫,必須在文件首#include<vector>
常用函數:
1.push_back 在數組的最后添加一個數據
2.pop_back 去掉數組的最后一個數據
3.at 得到編號位置的數據
4.begin 得到數組頭的指針
5.end 得到數組的最后一個單元+1的指針
6.front 得到數組頭的引用
7.back 得到數組的最后一個單元的引用
8.max_size 得到vector最大可以是多大
9.capacity 當前vector分配的大小
10.size 當前使用數據的大小
11.resize 改變當前使用數據的大小,如果它比當前使用的大,者填充默認值
12.reserve 改變當前vecotr所分配空間的大小
13.erase 刪除指針指向的數據項
14.clear 清空當前的vector
15.rbegin 將vector反轉后的開始指針返回(其實就是原來的end-1)
16.rend 將vector反轉構的結束指針返回(其實就是原來的begin-1)
17.empty 判斷vector是否為空
18.swap 與另一個vector交換數據
使用說明:
==================================
1. 構造函數
語法:
vector();
vector( size_type num, const TYPE &val );
vector( const vector &from );
vector( input_iterator start, input_iterator end );
C++ Vectors可以使用以下任意一種參數方式構造:
1. 無參數 - 構造一個空的vector,
2. 數量(num)和值(val) - 構造一個初始放入num個值為val的元素的Vector
3. vector(from) - 構造一個與vector from 相同的vector
4. 迭代器(start)和迭代器(end) - 構造一個初始值為[start,end)區間元素的Vector(注:半開區間).
舉例,下面這個實例構造了一個包含5個值為42的元素的Vector
vector<int> v1( 5, 42 );
實現2維數組:
如果一個向量的每一個元素是一個向量,則稱為二維向量,例如
vector<vector<int> >vv(3, vector<int>(4)); //這里,兩個“>”間的空格是不可少的
vector<vector<int> >vv(3, vector<int>(4)); //這里,兩個“>”間的空格是不可少的將構造一個二維向量vv,它含有三個元素,每個元素含有4個int型元素的向量。編譯器兩次調用vector的構造函數構造對象vv,第一次調用構造函數構造了一個無名的含有4個0的vector<int>對象:
[0] | [1] | [2] | [3] |
0 | 0 | 0 | 0 |
第二次調用構造函數,以這個無名向量為初值初始化它的三個元素,結果是:
vv | [0] | [1] | [2] | [3] |
[0] | 0 | 0 | 0 | 0 |
[1] | 0 | 0 | 0 | 0 |
[2] | 0 | 0 | 0 | 0 |
vv[i]表示第i(i=0,1,2)行的元素組成的向量。vv.size()的值是3,vv[1].size()的值是4.
一個二維向量每個元素的長度可以不同,例如
-
vector<vector<int> >vv;
-
for(int c = 1; c <= 3; c++)
-
vv.push_back(vector<int>(c,0));
vector<vector<int> >vv;
for(int c = 1; c <= 3; c++) vv.push_back(vector<int>(c,0));
上面代碼產生一個長度不同的二維向量,第一行只有1個元素,第二行有兩個,第三行三個。
運算符
語法:
v1 == v2
v1 != v2
v1 <= v2
v1 >= v2
v1 < v2
v1 > v2
v[]
C++ Vectors能夠使用標准運算符: ==, !=, <=, >=, <, 和 >. 要訪問vector中的某特定位置的元素可以使用 [] 操作符.
兩個vectors被認為是相等的,如果:
它們具有相同的容量
所有相同位置的元素相等.
vectors之間大小的比較是按照詞典規則.
===================================================
assign函數
語法:
void assign( input_iterator start, input_iterator end );
void assign( size_type num, const TYPE &val );
assign() 函數要么將區間[start, end)的元素賦到當前vector,或者賦num個值為val的元素到vector中.這個函數將會清除掉為vector賦值以前的內容.
==========================
at函數
語法:
TYPE at( size_type loc );
at() 函數 返回當前Vector指定位置loc的元素的引用. at() 函數 比 [] 運算符更加安全, 因為它不會讓你去訪問到Vector內越界的元素.
例如, 考慮下面的代碼:
vector<int> v( 5, 1 );
for( int i = 0; i < 10; i++ ) {
cout << "Element " << i << " is " << v[i] << endl;
}
這段代碼訪問了vector末尾以后的元素,這將可能導致很危險的結果.以下的代碼將更加安全:
vector<int> v( 5, 1 );
for( int i = 0; i < 10; i++ ) {
cout << "Element " << i << " is " << v.at(i) << endl;
}
取代試圖訪問內存里非法值的作法,at() 函數能夠辨別出訪問是否越界並在越界的時候拋出一個異常out_of_range.
====================
back 函數
語法:
TYPE back(),front();
front() 函數返回當前vector最首一個元素的引用
back() 函數返回當前vector最末一個元素的引用.例如:
vector<int> v;
for( int i = 0; i < 5; i++ ) {
v.push_back(i);
}
cout << "The first element is " << v.front()
<< " and the last element is " << v.back() << endl;
這段代碼產生如下結果:
The first element is 0 and the last element is 4
===============================
begin 函數
語法:
iterator begin();
begin()函數返回一個指向當前vector起始元素的迭代器(似乎是指向元素的指針) .例如,下面這段使用了一個迭代器來顯示出vector中的所有元素:
vector<int> v1( 5, 789 );
vector<int>::iterator it;
for( it = v1.begin(); it != v1.end(); it++ )
cout << *it << endl;
end 函數
語法:
iterator end();
end() 函數返回一個指向當前vector末尾元素的下一位置的迭代器.注意,如果你要訪問末尾元素,需要先將此迭代器自減1.
capacity 函數
語法:
size_type capacity();
capacity() 函數 返回當前vector在重新進行內存分配以前所能容納的元素數量.
clear 函數
語法:
void clear();
clear()函數刪除當前vector中的所有元素.
empty 函數
語法:
bool empty();
如果當前vector沒有容納任何元素,則empty()函數返回true,否則返回false.
例如,以下代碼清空一個vector,並按照逆序顯示所有的元素:
vector<int> v;
for( int i = 0; i < 5; i++ ) {
v.push_back(i);
}
while( !v.empty() ) {
cout << v.back() << endl;
v.pop_back();
}
erase 函數
語法:
iterator erase( iterator loc );
iterator erase( iterator start, iterator end );
erase函數要么刪作指定位置loc的元素,要么刪除區間[start, end)的所有元素.
返回值是指向刪除的最后一個元素的下一位置的迭代器.例如:
// 創建一個vector,置入字母表的前十個字符
vector<char> alphaVector;
for( int i=0; i < 10; i++ )
alphaVector.push_back( i + 65 );
int size = alphaVector.size();
vector<char>::iterator startIterator;
vector<char>::iterator tempIterator;
for( int i=0; i < size; i++ )
{
tartIterator = alphaVector.begin();
alphaVector.erase( startIterator );
// Display the vector
for( tempIterator = alphaVector.begin(); tempIterator != alphaVector.end(); tempIterator++ )
cout << *tempIterator;
cout << endl;
}
這段代碼將會顯示如下輸出:
BCDEFGHIJ
CDEFGHIJ
DEFGHIJ
EFGHIJ
FGHIJ
GHIJ
HIJ
IJ
J
pop_back
語法:
void pop_back();
pop_back()函數刪除當前vector最末的一個元素,例如:
vector<char> alphaVector;
for( int i=0; i < 10; i++ )
alphaVector.push_back( i + 65 );
int size = alphaVector.size();
vector<char>::iterator theIterator;
for( int i=0; i < size; i++ ) {
alphaVector.pop_back();
for( theIterator = alphaVector.begin(); theIterator != alphaVector.end(); theIterator++ )
cout << *theIterator;
cout << endl;
}
這段代碼將顯示以下輸出:
ABCDEFGHI
ABCDEFGH
ABCDEFG
ABCDEF
ABCDE
ABCD
ABC
AB
A
push_back 函數
語法:
void push_back( const TYPE &val );
push_back()添加值為val的元素到當前vector末尾
===============================
get_allocator 函數
語法:
allocator_type get_allocator();
get_allocator() 函數返回當前vector的內存分配器.在STL里面一般不會調用new或者alloc來分配內存,而且通過一個allocator對象的相關方法來分配.
示例:vector <int> v3( 3, 1, v2.get_allocator( ));//把V2的內存分配器作為一個參數參與構造V3。這樣,它們兩個用一個內存分配器了。
=============================
insert 函數
語法:
iterator insert( iterator loc, const TYPE &val );
void insert( iterator loc, size_type num, const TYPE &val );
void insert( iterator loc, input_iterator start, input_iterator end );
insert() 函數有以下三種用法:
在指定位置loc前插入值為val的元素,返回指向這個元素的迭代器,
在指定位置loc前插入num個值為val的元素
在指定位置loc前插入區間[start, end)的所有元素 .
舉例:
//創建一個vector,置入字母表的前十個字符
vector<char> alphaVector;
for( int i=0; i < 10; i++ )
alphaVector.push_back( i + 65 );
//插入四個C到vector中
vector<char>::iterator theIterator = alphaVector.begin();
alphaVector.insert( theIterator, 4, 'C' );
//顯示vector的內容
for( theIterator = alphaVector.begin(); theIterator != alphaVector.end(); theIterator++ )
cout << *theIterator;
這段代碼將顯示:
CCCCABCDEFGHIJ
===============================
max_size 函數
語法:
size_type max_size();
max_size() 函數返回當前vector所能容納元素數量的最大值(譯注:包括可重新分配內存).
rbegin 函數
語法:
reverse_iterator rbegin();
rbegin函數返回指向當前vector末尾的逆迭代器.(譯注:實際指向末尾的下一位置,而其內容為末尾元素的值,詳見逆代器相關內容)
示例:
vector<int>v1;
for(int i=1;i<=5;i++)
{
v1.push_back(i);
}
vector<int>::reverse_iterator pos;
pos=v1.rbegin();
cout<<*pos<<" ";
pos++;
cout<<*pos<<endl;
輸出結果為:5 4
rend 函數
語法:
reverse_iterator rend();
rend()函數返回指向當前vector起始位置的逆迭代器.
示例:
vector<int>v1;
for(int i=1;i<=5;i++)
{
v1.push_back(i);
}
vector<int>::reverse_iterator pos;
pos=v1.rend();
pos--;
cout<<*pos<<" ";
pos--;
cout<<*pos<<endl;
輸出結果為:1 2
reserve 函數
語法:
void reserve( size_type size );
reserve()函數為當前vector預留至少共容納size個元素的空間.(譯注:實際空間可能大於size)
resize 函數
語法:
void resize( size_type size, TYPE val );
resize() 函數改變當前vector的大小為size,且對新創建的元素賦值val
resize 與reserve的區別
reserve是容器預留空間,但並不真正創建元素對象,在創建對象之前,不能引用容器內的元素,因此當加入新的元素時,需要用push_back()/insert()函數。
resize是改變容器的大小,並且創建對象,因此,調用這個函數之后,就可以引用容器內的對象了,因此當加入新的元素時,用operator[]操作符,或者用迭代器來引用元素對象。再者,兩個函數的形式是有區別的,reserve函數之后一個參數,即需要預留的容器的空間;resize函數可以有兩個參數,第一個參數是容器新的大小,第二個參數是要加入容器中的新元素,如果這個參數被省略,那么就調用元素對象的默認構造函數。
初次接觸這兩個接口也許會混淆,其實接口的命名就是對功能的絕佳描述,resize就是重新分配大小,reserve就是預留一定的空間。這兩個接口即存在差別,也有共同點。下面就它們的細節進行分析。
為實現resize的語義,resize接口做了兩個保證:
一是保證區間[0, new_size)范圍內數據有效,如果下標index在此區間內,vector[indext]是合法的。
二是保證區間[0, new_size)范圍以外數據無效,如果下標index在區間外,vector[indext]是非法的。
reserve只是保證vector的空間大小(capacity)最少達到它的參數所指定的大小n。在區間[0, n)范圍內,如果下標是index,vector[index]這種訪問有可能是合法的,也有可能是非法的,視具體情況而定。
resize和reserve接口的共同點是它們都保證了vector的空間大小(capacity)最少達到它的參數所指定的大小。
因兩接口的源代碼相當精簡,以至於可以在這里貼上它們:
void resize(size_type new_size) { resize(new_size, T()); }
void resize(size_type new_size, const T& x) {
if (new_size < oldsize)
erase(oldbegin + new_size, oldend); // erase區間范圍以外的數據,確保區間以外的數據無效
else
insert(oldend, new_size - oldsize, x); // 填補區間范圍內空缺的數據,確保區間內的數據有效
示例:
#include<iostream>
#include<vector>
using namespace std;
void main()
{
vector<int>v1;
for(int i=1;i<=3;i++)
{
v1.push_back(i);
}
v1.resize(5,8);//多出的兩個空間都初始化為8,
for(i=0;i<v1.size();i++)//resize與reserver並不會刪除原先的元素以釋放空間
{
cout<<v1[i]<<" ";
}
cout<<endl;
v1.reserve(7);// 新元素還沒有構造,
for(i=0;i<7;i++)
{
cout<<v1[i]<<" ";//當i>4,此時不能用[]訪問元素
}
cout<<endl;
cout<<v1.size()<<endl;
cout<<v1.capacity()<<endl;
}
輸出結果為:
1 2 3 8 8
1 2 3 8 8 -842150451 -842150451
5
7
size函數
語法:
size_type size();
size() 函數返回當前vector所容納元素的數目
swap 函數
語法:
void swap( vector &from );
swap()函數交換當前vector與vector from的元素
示例:
vector<int>v1,v2;
for(int i=1;i<=3;i++)
{
v1.push_back(i);
v2.push_back(i);
}
v2.push_back(4);
v2.push_back(5);
v1.swap(v2);
for(int j=0;j<v1.size();j++)
{
cout<<v1[j]<<" ";
}
cout<<endl;
for(int k=0;k<v2.size();k++)
{
cout<<v2[k]<<" ";
}
cout<<endl;
輸出結果為:
1 2 3 4 5
1 2 3
======================
======================
4.內存管理與效率
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<int>。沒有使用reserve,你可以像這樣來做:
vector<int> v;
for (int i = 1; i <= 1000; ++i) v.push_back(i);
在大多數STL實現中,這段代碼在循環過程中將會導致2到10次重新分配。(10這個數沒什么奇怪的。記住vector在重新分配發生時一般把容量翻倍,而1000約等於210。)
把代碼改為使用reserve,我們得到這個:
vector<int> 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<int>(ivec).swap(ivec);
表達式vector<int>(ivec)建立一個臨時vector,它是ivec的一份拷貝:vector的拷貝構造函數做了這個工作。但是,vector的拷貝構造函數只分配拷貝的元素需要的內存,所以這個臨時vector沒有多余的容量。然后我們讓臨時vector和ivec交換數據,這時我們完成了,ivec只有臨時變量的修整過的容量,而這個臨時變量則持有了曾經在ivec中的沒用到的過剩容量。在這里(這個語句結尾),臨時vector被銷毀,因此釋放了以前ivec使用的內存,收縮到合適。
3》用swap方法強行釋放STL Vector所占內存
template < class T> void ClearVector( vector<T>& v )
{
vector<T>vtTemp;
vtTemp.swap( v );
}
如
vector<int> v ;
nums.push_back(1);
nums.push_back(3);
nums.push_back(2);
nums.push_back(4);
vector<int>().swap(v);
/* 或者v.swap(vector<int>()); */
/*或者{ std::vector<int> tmp = v; v.swap(tmp); }; //加大括號{ }是讓tmp退出{ }時自動析構*/
5.Vector 內存管理成員函數的行為測試
C++ STL的vector使用非常廣泛,但是對其內存的管理模型一直有多種猜測,下面用實例代碼測試來了解其內存管理方式,測試代碼如下:
#include <iostream>
#include <vector>
using namespace std;
int main()
{
vector<int> iVec;
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //1個元素, 容器容量為1
iVec.push_back(1);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //2個元素, 容器容量為2
iVec.push_back(2);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //3個元素, 容器容量為4
iVec.push_back(3);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //4個元素, 容器容量為4
iVec.push_back(4);
iVec.push_back(5);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //5個元素, 容器容量為8
iVec.push_back(6);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //6個元素, 容器容量為8
iVec.push_back(7);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //7個元素, 容器容量為8
iVec.push_back(8);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //8個元素, 容器容量為8
iVec.push_back(9);
cout << "容器 大小為: " << iVec.size() << endl;
cout << "容器 容量為: " << iVec.capacity() << endl; //9個元素, 容器容量為16
/* vs2005/8 容量增長不是翻倍的,如
9個元素 容量9
10個元素 容量13 */
/* 測試effective stl中的特殊的交換 swap() */
cout << "當前vector 的大小為: " << iVec.size() << endl;
cout << "當前vector 的容量為: " << iVec.capacity() << endl;
vector<int>(iVec).swap(iVec);
cout << "臨時的vector<int>對象 的大小為: " << (vector<int>(iVec)).size() << endl;
cout << "臨時的vector<int>對象 的容量為: " << (vector<int>(iVec)).capacity() << endl;
cout << "交換后,當前vector 的大小為: " << iVec.size() << endl;
cout << "交換后,當前vector 的容量為: " << iVec.capacity() << endl;
return 0;
}
6.vector的其他成員函數
c.assign(beg,end):將[beg; end)區間中的數據賦值給c。
c.assign(n,elem):將n個elem的拷貝賦值給c。
c.at(idx):傳回索引idx所指的數據,如果idx越界,拋出out_of_range。
c.back():傳回最后一個數據,不檢查這個數據是否存在。
c.front():傳回地一個數據。
get_allocator:使用構造函數返回一個拷貝。
c.rbegin():傳回一個逆向隊列的第一個數據。
c.rend():傳回一個逆向隊列的最后一個數據的下一個位置。
c.~ vector <Elem>():銷毀所有數據,釋放內存。
7.備注:在用vector的過程中的一些問題,特此列出討論:
1)
vector <int > a;
int b = 5;
a.push_back(b);
此時若對b另外賦值時不會影響a[0]的值
2)
vector <int*> a;
int *b;
b= new int[4];
b[0]=0;
b[1]=1;
b[2]=2;
a.push_back(b);
delete b; //釋放b的地址空間
for(int i=0 ; i <3 ; i++)
{
cout<<a[0][i]<<endl;
}
此時輸出的值並不是一開始b數組初始化的值,而是一些無法預計的值.
分析:根據1) 2)的結果,可以想到,在1)中, 往a向量中壓入的是b的值,即a[0]=b,此時a[0]和b是存儲在兩個不同的地址中的.因此改變b的值不會影響a[0];而在2)中,因為是把一個地址(指針)壓入向量a,即a[0]=b,因此釋放了b的地址也就釋放了a[0]的地址,因此a[0]數組中存放的數值也就不得而知了.