STL Vector容器
STL Vector容器
Vector容器簡介
vector是將元素置於一個動態數組中加以管理的容器。
vector可以隨機存取元素(支持索引值直接存取, 用[]操作符或at()方法,這個等下會詳講)。
vector尾部添加或移除元素非常快速。但是在中部或頭部插入元素或移除元素比較費時
頭文件:#include<vector>
vector對象的默認構造
vector采用模板類實現,
vector對象的默認構造形式: vector<T> vecT;
示例:
容器中可以添加常規數據類型:
vector<int> vecInt; //一個存放int的vector容器。
vector<float> vecFloat; //一個存放float的vector容器。
vector<string> vecString; //一個存放string的vector容器。
尖括號內還可以設置指針類型或自定義類型:
Class CA{};
vector<CA*> vecpCA; //用於存放CA對象的指針的vector容器。
vector<CA> vecCA;
用於存放CA對象的vector容器。由於容器元素的存放是按值復制
vector中不能存放引用類型!!!!!!!!
關於 vector 中不能存放引用,這是一個在初始C++時候就應該知道的問題,但是我居然沒註意,還好及時發現了。
《C++ primer》上說 vector 中不能存放引用的原因是:引用不支持一般意義上的賦值操作,而 vector中元素的兩個要求是:
1.元素必須能賦值
2.元素必須能復制
- int a = 1;
- int c = 2;
- int & b = a;
- b = c;
如上述例子中 ,b = c,不是一般意義上的賦值操作,因為 b 元素時不存在的,對 b 元素取地址,取到的是 a 的地址,
此處的賦值操作也會將值賦給 a,而不是 b 本身,因此,vector 的元素不能是引用
我自己試了一下,將發現在 vector 中存放引用報錯的原因類似如下:
- int & * p;
即,不允許定義引用類型的指針。
vector末尾的添加移除操作
理論知識:
在末尾添加元素: push_back(element)
在末尾移除元素: pop_back()
示例:
vector<int> vecInt; //在容器尾部加入一個元素
vecInt.push_back(1);
vecInt.push_back(3);
vecInt.push_back(5);
vecInt.push_back(7);
vecInt.push_back(9);
//移除容器中最後一個元素
vecInt.pop_back();
vecInt.pop_back();
//打印結果
1 3 5
vector的數據存取
理論知識
vec.at(idx); 返回索引idx所指的數據,如果idx越界,拋出 out_of_range異常。
vec[idx]; 返回索引idx所指的數據,越界時,運行直接報錯
vec.front(); 返回第一個元素的引用
vec.back(); 返回最後一個元素的引用
示例:
vector<int> vecInt; //假設包含1 ,3 ,5 ,7 ,9
vecInt.at(2) == vecInt[2] //5
vecInt.at(2) = 8; 或 vecInt[2] = 8;
vecInt 就包含 1, 3, 8, 7, 9值
int iF = vector.front(); //iF==1
int iB = vector.back(); //iB==9
vector.front() = 11; //vecInt包含{11,3,8,7,9}
vector.back() = 19; //vecInt包含{11,3,8,7,19}
叠代器
概念
叠代器是一種檢查容器內元素並遍歷元素的數據類型。
標準庫為每一種標準容器(包括 vector)定義了一種叠代器類型。
叠代器 類型提供了比下標操作更通用化的方法:所有的標準庫容器都定義了相應的叠代器類型,而只有少數的容器支持下標操作。因為叠代器對所有的容器都適用,現 代 C++ 程序更傾向於使用叠代器而不是下標操作訪問容器元素,即使對支持下 標操作的 vector 類型也是這樣。
每種容器類型都定義了自己的叠代器類型,如 vector:
vector<數據類型>::iterator 變量名; //正向叠代器
Vector<數據類型>::const_iterator 變量名; //只讀正向叠代器
vector<數據類型>::reverse_iterator 變量名; //逆向叠代器
Vector<數據類型>::const_reverse_iterator 變量名; //只讀逆向叠代器
叠代器可以看做是一個指針
叠代器的分類
輸入叠代器:
也有叫法稱之為“只讀叠代器”,它從容器中讀取元素,只能一次讀入一個元素向前移動,只支持一遍算法,同一個輸入叠代器不能兩遍遍歷一個序列。(了解)
輸出叠代器:
也有叫法稱之為“只寫叠代器”,它往容器中寫入元素,只能一次寫入一個元素向前移動,只支持一遍算法,同一個輸出叠代器不能兩遍遍歷一個序列。(了解)
正向叠代器:
組合輸入叠代器和輸出叠代器的功能,還可以多次解析一個叠代器指定的位置,可以對一個值進行多次讀/寫。(了解)
雙向叠代器:
雙向叠代器具有正向叠代器的全部功能。另外它還可以利用自減操作符operator--向後一次移動一個位置。由list容器中返回的叠代器都是雙向的。(重點)
隨機訪問叠代器:
具有雙向叠代器的所有功能,還可以向前向後跳過任意個位置,可以直接訪問容器中任何位置的元素。(重點)
目前本系列教程所用到的容器,都支持雙向叠代器或隨機訪問叠代器,下面將會詳細介紹這兩個類別的叠代器。
雙向叠代器與隨機訪問叠代器
雙向叠代器支持的操作:
* 自增,自減:it++, ++it, it--, --it
* 解引用:*it * 賦值:itA = itB,
* 比較:itA == itB,itA != itB
其中list, set, multiset, map, multimap支持雙向叠代器。
隨機訪問叠代器支持的操作:
* 雙向叠代器支持的所有的操作
* 算數操作:it+=i, it-=i, it+i(或it=it+i) * 隨機訪問元素:it[i]
* 關系操作符:itA<itB, itA<=itB, itA>itB, itA>=itB 的功能。
其中vector,deque支持隨機訪問叠代器。
begin 和 end 操作
每種容器都定義了一對命名為 begin 和 end 的函數,用於返回叠代器。如果容器中有元素的話,由 begin 返回的叠代器指向第一個元素:
vector<int>::iterator iter = ivec.begin();
上述語句把 iter 初始化為由名為 vector 操作返回的值。假設 vector 不空,初始化後, iter 即指該元素為 ivec[0]。
由 end 操作返回的叠代器指向 vector 的“末端元素的下一個”。 我們稱之為“超出末端叠代器”(off-the-end iterator) 。表明它指向了一個不存在的元素。如果容器為空,begin 返回的叠代器與 end 返回的叠代器相同。
rbegin 和 rend 操作
每種容器都定義了一對命名為 rbegin 和 rend 的函數,用於返回逆序叠代器。如 果容器中有元素的話,由 rbegin 返回的叠代器指向容器的最後第一個元素:
vector<int>::reverse_iterator riter = ivec.rbegin();
上述語句把 riter 初始化為由名為 vector 操作返回的值。假設 vector 不空,初始化後, riter 即指該元素為 riter [size()-1]。 由 rend 操作返回的叠代器指向 vector 的第一個元素前面的位置。表明它指向了一個不存在的元素。如果 容器 為空,rbegin 返回的叠代器與 rend 返回的叠代器相同。
叠代器的自增和解引用運算
叠代器類型定義了一些操作來獲取叠代器所指向的元素,並允許程序員將叠代器從一個元素移動到另一個元素。
叠代器類型可使用解引用操作符(dereference operator)(*)來訪問叠代器所指向的元素:
*iter = 0;
解引用操作符返回叠代器當前所指向的元素。假設 iter 指向 vector 對象 ivec 的第一元素,那麽 *iter 和 ivec[0] 就是指向同一個元素。上面這個語句的效果就是把這個元素的值賦為 0。
叠代器使用自增操作符向前移動叠代器指向容器中下一個元素。從邏輯上說,叠代器的自增操作和 int 型對象的自增操作類似。對 int 對象來說,操作結果就是把 int 值“加 1”,而對叠代器對象則是把容器中的叠代器“向前移動一個位置”。因此,如果 iter 指向第一個元素,則 ++iter 指向第二個元素。
由於 end/rend 操作返回的叠代器不指向任何元素,因此不能對它進行解引用或自增操作。
vector與叠代器的配合使用
理論知識:
vector<T>::iterator 正向叠代器
vector<T>::reverse_iterator 逆向叠代器
c.begin() 返回一個叠代器,它指向容器 c 的第一個元素
c.end() 返回一個叠代器,它指向容器 c 的最後一個元素的 下一個
c.rbegin() 返回一個逆序叠代器,它指向容器 c 的最後一個元素
c.rend() 返回一個逆序叠代器,它指向容器 c 的第一個元素前面的位置
示例:
vector<int> vecInt; //假設包含1,3,5,7,9元素
vector<int>::iterator it; //聲明容器vector<int>的叠代器。
it = vecInt.begin(); // *it == 1
++it; //或者it++; *it == 3 ,前++的效率比後++的效率高,前++返回引用,後++返回值。
it += 2; //*it == 7
it = it+1; //*it == 9
++it; // it == vecInt.end(); 此時不能再執行*it,會出錯!
正向遍歷:
for(vector<int>::iterator it=vecInt.begin(); it!=vecInt.end(); ++it)
{
int iItem = *it;
cout << iItem; //或直接使用 cout << *it;
}
這樣子便打印出1 3 5 7 9
逆向遍歷:
for(vector<int>::reverse_iterator rit=vecInt.rbegin(); rit!=vecInt.rend(); ++rit) //註意,小括號內仍是++rit
{
int iItem = *rit;
cout << iItem; //或直接使用cout << *rit;
}
此時將打印出9,7,5,3,1
註意,這裏叠代器的聲明采用vector<int>::reverse_iterator,而非vector<int>::iterator。
叠代器還有其它兩種聲明方法:
vector<int>::const_iterator 與 vector<int>::const_reverse_iterator
以上兩種分別是vector<int>::iterator 與vector<int>::reverse_iterator 的只讀形式,使用這兩種叠代器時,不會修改到容器中的值。
備註:不過容器中的insert和erase方法僅接受這四種類型中的iterator,其它三種不支持。
《Effective STL》建議我們盡量使用iterator取代const_iterator、reverse_iterator和const_reverse_iterator。
vector對象的帶參數構造
理論知識
vector v(beg,end)
構造函數將[beg, end)區間中的元素拷貝 給本身。註意該區間是左閉右開的區間。
vector v(n)
創建有n個初始化元素的容器v
vector v(n,elem)
構造函數將n個elem拷貝給本身。
vector v(const vector &vec)
拷貝構造函數
示例:
int iArray[] = {0,1,2,3,4};
vector<int> vecIntA( iArray, iArray+5 ); // 0,1,2,3,4
用構造函數初始化容器vecIntB:
vector<int> vecIntB ( vecIntA.begin() , vecIntA.end() ); // 0,1,2,3,4
vector<int> vecIntB ( vecIntA.begin() , vecIntA.begin()+3 ); // 0,1,2
此代碼運行後,容器vecIntB就存放3個元素,每個元素的值是9。
vector<int> vecIntC(3,9); // 9,9,9
vector<int> vecIntD(vecIntA); // 0,1,2,3,4
使用for_each和使用lambda函數來進行遍歷, 需要包含<algorithm>頭文件
for_each(vecIntA.begin(), vecIntA.end(), [](const int & val){ cout << val << endl;});
vector的賦值
理論知識
c.assign(b,e)
重新設置 c 的元素:將叠代器 b 和 e 標記的範圍 內所有的元素復制到c 中。 b 和 e 必須不是指向 c 中元素的叠代器。註意該區間是左閉右開的區間。
c.assign(n,t)
將容器 c 重新設置為存儲 n 個值為 t 的元素。
c1 = c2
刪除容器 c1 的所有元素, 然後將 c2 的元素復制 給 c1。 c1 和c2的類型(包括容器類型和元素 類型)必須相同。
c1.swap(c2)
交換內容:調用完該函數後,c1 中存放的是 c2 原 來的元素,c2變c1。c1 和 c2 的類型必須相同。該函數的執行速度通常要比將 c2 復制到 c1的操作快。
示例:
vector<int> vecIntA, vecIntB, vecIntC;
int iArray[] = {0,1,2,3,4};
vecIntA.assign(iArray,iArray+5);
vecIntB.assign( vecIntA.begin(), vecIntA.end() ); //用其它容器的叠代器作參數。
vecIntC.assign(3,9);
vector<int> vecIntD;
vecIntD = vecIntA;
vecIntA.swap(vecIntD);
vector的插入
理論知識
vec.insert(pos, elem)
在pos位置插入一個elem元素的拷貝,返回新數據的位置
vec.insert(pos, n, elem)
在pos位置插入n個elem數據,無返回值。
vec.insert(pos, beg, end)
在pos位置插入[beg,end)區間的數據, 無返回值
簡單案例
vector<int> vecA;
vector<int> vecB;
vecA.push_back(1);
vecA.push_back(3);
vecA.push_back(5);
vecA.push_back(7);
vecA.push_back(9);
vecB.push_back(2);
vecB.push_back(4);
vecB.push_back(6);
vecB.push_back(8);
vecA.insert(vecA.begin(), 11); //{11, 1, 3, 5, 7, 9}
vecA.insert(vecA.begin()+1,2,33); //{11,33,33,1,3,5,7,9}
vecA.insert(vecA.begin() , vecB.begin() , vecB.end() ); //{2,4,6,8,11,33,33,1,3,5,7,9}
vector的刪除
理論知識
vector.clear()
移除容器的所有數據
vec.erase(pos)
刪除pos位置的數據,返回下一個數據的位置。
vec.erase(beg,end)
刪除[beg,end)區間的數據,返回下一個數據的位置。
簡單案例:
刪除區間內的元素
vecInt是用vector<int>聲明的容器,現已包含按順序的1,3,5,6,9元素。
vector<int>::iterator itBegin=vecInt.begin()+1;
vector<int>::iterator itEnd=vecInt.begin()+2;+
vecInt.erase(itBegin,itEnd);
//此時容器vecInt包含按順序的1,6,9三個元素。
假設 vecInt 包含1,3,2,3,3,3,4,3,5,3,刪除容器中等於3的元素
for(vector<int>::iterator it=vecInt.being(); it!=vecInt.end(); ) //小括號裏不需寫 ++it
{
if(*it == 3)
{
it = vecInt.erase(it); //以叠代器為參數,刪除元素3,並把數據刪除 後的下一個元素位置返回給叠代器。此時,不 執行 ++it;
}
else
{
++it;
}
}
//刪除vecInt的所有元素
vecInt.clear(); //容器為空
小結
容器vector的具體用法(包括叠代器的具體用法):
vertor簡介
vector使用之前的準備
vector對象的構造函數(帶參,不帶參)
vector末尾的添加移除操作 vector的數據存取
叠代器的簡介,雙向叠代器與隨機訪問叠代器
vector與叠代器的配合使用
vector的賦值
vector的大小
vector的插入
vector的刪除。
STL Vector容器