一.構造函數
vector類提供了多個構造函數,可以根據需要靈活地使用這些函數建立vector對象。
1. explicit vector (const allocator_type& alloc = allocator_type());
預設構造函數。建立一個空的vector對象。
alloc是一個預設參數,用來指定要使用的空間擴充卡。不需要對其進行指派,使用STL提供的預設的空間擴充卡即可。預設參數詳見:百度百科——預設參數、【C++】預設參數
比如:vector<int> temp; 使用這樣的方式來建立vector對象時會調用該預設構造函數。
隻是聲明但沒有初始化,編譯器會将不會為其配置設定記憶體空間。需要使用該temp對象調用成員函數resize()來申請空間,或者調用push_back()方法向容器插入元素。
2. vector (size_type n, const value_type& val, const allocator_type& alloc = allocator_type());
建立一個包含n個元素的vector對象,其中每個元素都被初始化為value。
其中,val也是一個預設參數,如果沒有為其傳入實參,那麼預設使用0來初始化這些元素。
比如:vector<int> temp(3); //temp包含3個int型的0
vector<int> temp1(4, 1); //temp1包含4個int型的1
3. template <class InputIterator>
vector (InputIterator first, InputIterator last, const allocator_type& alloc = allocator_type());
建立一個vector對象,并使用一組有序資料中部分元素來初始化這個vector對象。
first和last是兩個疊代器,分别表示所選的部分元素的起始位址和結束位址。
比如:int a[5] = {1, 2, 3, 4, 5}; //有序資料是一個int型數組
vector<int>temp(a, a+4); //使用數組a的前四個元素初始化容器temp
vector<int>temp1(temp.begin(), temp.end()-1); //此時,有序資料是指int型容器temp,這裡表示使用容器temp除最後一個元素外的所有元素來初始化temp1,最終結果是temp1有三個元素,分别是1,2,3
4. vector (const vector& x, const allocator_type& alloc);
拷貝構造函數。使用一個vector對象來初始化一個新的vector對象。
比如在第3點中,我們使用temp來初始化temp1:
vector<int>temp1(temp); //temp1包含四個元素,分别是1,2,3,4
另外c++11新增了移動構造函數和初始化器清單構造函數,詳見std::vector::vector
二.生成疊代器
1. 正向疊代器
①iterator begin() noexcept; //傳回指向容器第一個元素的疊代器
②const_iterator begin() const noexcept; //同①,但容器中的元素是const類型的
③iterator end() noexcept; //傳回指向容器最後一個元素後面一個元素的疊代器(假想的一個元素)
④const_iterator end() const noexcept; //同③,但容器中的元素是const類型的
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp(5, 10);
vector<int>::iterator my_iterator = temp.begin();
for ( ; my_iterator != temp.end(); my_iterator++)
{
cout << *my_iterator << endl;
}
}
編譯運作結果:
從示例可以看出,疊代器的使用與指針類似。但是指針本身是個記憶體位址,可以列印出來;而輸出疊代器本身是沒有意義的。
疊代器實際上是個iterator類型的變量,以及下面的反向疊代器(reverse_iterator類型的變量),都是vector類的資料成員,在調用這些生成疊代器的函數時,需要使用類型比對的變量來接收函數傳回值。
對疊代器本身進行加減操作類似于對數組名進行加減操作,将改變疊代器的指向。對于正向疊代器而言,對其進行加操作會使其向後移動,即指向下标更大的元素。
2. 反向疊代器
①reverse_iterator rbegin() noexcept; //傳回指向容器最後一個元素的疊代器(反向疊代器)
②const_reverse_iterator rbegin() const noexcept; //同①
③reverse_iterator rend() noexcept; //傳回指向容器第一個元素的疊代器
④const_reverse_iterator rend() const noexcept;
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
vector<int>::reverse_iterator my_iterator = temp.rbegin();
cout << *(my_iterator + 1) << endl; //利用反向疊代器輸出倒數第二個元素
my_iterator = temp.rend();
cout << *(my_iterator - 1) << endl; //利用反向疊代器輸出第一個元素
}
編譯運作結果:
從示例可以看出,rend()函數傳回的是指向容器最後一個元素的疊代器,這一點與end()是有差別的。
另外,對于反向疊代器而言,對其進行加操作,會使其向前移動,即指向下标更小的元素。
三.向末尾添加或删除元素
void push_back (const value_type& val);
void push_back (value_type&& val);
void pop_back();
push_back()函數可以在目前容器的最後一個元素後面添加一個元素,實作容器的動态拓展。
變量類型value_type是容器中元素的類型,在容器中定義為其第一個模闆參數( T )的别名,val是待添加的元素。模闆類vector提供了push_back()函數的重載,用于操作const類型的元素。
pop_back()函數可以删除目前容器的最後一個元素,此時容器的大小也會減1。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
for (int i = 0; i < 5; i++)
{
temp.push_back(i); //向容器temp填入五個int型元素0,1,2,3,4
}
temp.pop_back(); //删除最後一個元素,隻調用一次,是以删除 4
for (vector<int>::iterator my_iterator = temp.begin(); my_iterator != temp.end(); my_iterator++)
{
cout << *my_iterator << endl;
}
}
編譯運作結果:
四.插入元素
前面我們使用成員函數push_back()向容器最後面添加元素,另外vector還提供了insert()方法用于向容器的任意位置插入元素,insert()插入元素不會覆寫,而是将該位置之前的元素和後面所有的元素向後移動。不僅可以有效地增加元素數量,更關鍵的是提供了更加有效的、友善的對資料的操作方式。
但是容器的是以動态數組作為基礎實作的,插值的操作會導緻對後面的所有的元素也進行移位,是以強大的功能性是以性能作為代價的。C++11新特性(26)- 容器的insert成員
vector類提供了insert()函數的多個重載,用于實作不同的插入方式。
1. iterator insert (const_iterator position, const value_type& val);
在指定位置處插入單個元素。調用完後,容器position處的元素将變為val。
position是待插入的元素在容器中的位置,由疊代器指定,是以該參數需要由第二點中的某個函數傳回。val是待插入的元素。
比如:temp.insert(temp.begin() + 2, 6); //在容器temp的第三個元素處插入一個6
2. iterator insert (const_iterator position, size_type n, const value_type& val);
在指定位置處插入多個相同的元素。
n表示待插入的元素的數量。
比如:temp.insert(temp.begin() + 2, 3, 4); //在容器temp的第三四五個元素處插入三個4
3. template <class InputIterator>
iterator insert (const_iterator position, InputIterator first, InputIterator last);
在指定位置處插入另一容器的部分元素。這部分元素由另一容器的兩個疊代器指定。
比如:temp1.insert( temp1.begin()+2, temp.begin()+3, temp.end() ); //将容器temp除了前三個元素外的所有元素插入到容器temp1的第二個元素後面。
4. iterator insert (const_iterator position, value_type&& val);
将其他容器中的某個元素移動到指定位置。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp,temp1;
for (int i = 0; i < 5; i++)
temp.push_back(i);
temp1.insert(temp1.begin(), move(*(temp.begin() + 2)));
for (vector<int>::iterator my_iterator = temp.begin(); my_iterator != temp.end(); my_iterator++)
{
cout << *my_iterator << endl;
}
for (vector<int>::iterator my_iterator = temp1.begin(); my_iterator != temp1.end(); my_iterator++)
{
cout << *my_iterator << endl;
}
}
編譯運作結果:
注意:move()函數移動的是具體的值,所有不能傳入疊代器作為參數,而應使用解引用取到的具體的元素的值。
5. iterator insert (const_iterator position, initializer_list<value_type> il);
将初始化清單il中的元素插入到容器指定位置處。il是使用聚合法表示的元素。
比如:temp.insert(temp.begin(), {4, 5, 6}); //在容器temp開頭插入三個int型元素4,5,6
五.删除元素
iterator erase (iterator position);
iterator erase (const_iterator first, const_iterator last);
成員函數erase()有兩個重載,可用于從容器中删除某個元素,或删除一連串元素,其中這一連串的元素是由兩個疊代器指定的。
比如:temp.erase( temp.begin() + 2); //删除容器temp第三個元素
temp.erase( temp.begin() + 2, temp.end() ); //删除容器temp第三個及後面所有元素
六.清空容器
void clear() noexcept;
成員函數clear()用于将容器裡面的所有元素清空,調用完容器的大小将會為0。
對于一個容器來說,如果要對其多次重用,需要在每次使用後将其清空,因為常用到的push_back()函數隻會向容器後面添加元素,多次重用時,容器中以前的資料是不會被覆寫的。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
for (int i = 0; i < 500; i++)
{
temp.push_back(1);
}
temp.clear();
temp.push_back(2020);
for (vector<int>::iterator my_iterator = temp.begin(); my_iterator != temp.end(); my_iterator++)
{
cout << *my_iterator << endl;
}
}
編譯運作結果:
七.測試容器是否為空
bool empty() const;
成員函數empty()并不是清空容器,而是判斷容器是否為空。它不會對容器内的元素做任何修改,清空容器見第六點。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
if (!temp.empty())
{
cout << temp[0] << endl;
}
else
{
cout << "empty" << endl;
}
}
編譯運作結果:
八.傳回引用(通路元素)
1. 傳回第一個元素的引用
reference front();
const_reference front() const;
成員函數front()用于傳回容器第一個元素的引用。傳回類型reference和const_reference是對容器中元素類型的引用,如果元素是int類型,那麼實際傳回類型是int&。
前面生成疊代器的函數傳回的是指向元素的疊代器,傳回引用的函數傳回的是指定元素的值。同樣,模闆類vector提供了對front()函數的重載,根據容器存儲的元素是否是const類型自動選擇對應的函數。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
vector<int>::reverse_iterator my_iterator = temp.rend();
if (temp.front() == *(my_iterator - 1))
{
cout << "yes" << endl;
}
int&b = temp.front();
b = 10;
cout << temp[0] << endl;
}
編譯運作結果:
使用front()相當于對begin()擷取的疊代器解引用,如表達式temp.front() = *( temp.begin() )的結果為真
2. 傳回最後一個元素的引用
reference back();
const_reference back() const;
成員函數back()用于傳回容器最後一個元素的引用。用法與back()一緻,詳見front()。
3. 傳回任意位置元素的引用——at()函數
reference at (size_type n);
const_reference at (size_type n) const;
前面的成員函數back()和front()隻能傳回容器第一個和最後一個元素的引用,而at()函數可以傳回容器中任意一個元素的引用。
傳回類型在前面front()已經提過,不再贅述;參數n表示所要通路的元素在容器中的位置(即下标),也是從0開始。
注意:at()函數會自動檢查n是否在容器元素的下标範圍内,如果n大于或等于其大小,則引發out_of_range異常。而同樣是通路元素的值,通過下标通路則不會檢查。at()函數可以有效的避免下标通路可能存在的通路越界問題。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
cout << temp.at(10) << endl;;
}
編譯運作結果:
4. 傳回任意位置元素的引用——運算符[]重載
reference operator[] (size_type n);
const_reference operator[] (size_type n) const;
該運算符函數與前面的at()函數有幾乎一樣的功能,但是正如前面所說,如果n超過了容器目前的大小,該運算符函數會引發out_of_range異常,而at()不會。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
for (int i = 0; i < temp.size(); i++)
cout << temp.operator[](i) << endl; //顯示調用[]運算符重載
cout << temp[0] << endl; //隐式調用[]運算符重載
編譯運作結果:
另外,除了這些成員函數外,還可以通過對疊代器解引用通路元素的值,這一點在前面多出示例中使用過。
九.指派(複制、拷貝)
對容器指派使用新的元素替換容器目前的元素,容器的大小也會被相應的修改。容器有兩種重新指派的方式,重載的指派運算符和assign()成員函數。
參見第一部分中的拷貝構造函數。
1. 指派運算符重載
①vector& operator= (const vector& x);
将容器x中的所有元素複制到等号左側容器中,x的修飾詞const確定複制時x内的元素不被修改
②vector& operator= (vector&& x);
③vector& operator= (initializer_list<value_type> il);
使用初始化清單對容器指派。該方法将初始化清單il中的元素全部複制到目前容器中,替換已有的元素。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 }; //調用清單初始化構造函數建立vector對象temp
vector<int> temp1 = { 0,1 }; //調用清單初始化構造函數建立vector對象temp1
temp1.operator=({0,1,2}); //(顯式)調用指派運算符重載③對temp1指派
cout << temp1[2] << endl;
temp1 = { 1,2,3,4,5 }; //(隐式)調用指派運算符重載③對temp1指派
cout << temp[3] << endl;
}
編譯運作結果:
2. 成員函數assign()
①template <class InputIterator> void assign (InputIterator first, InputIterator last);
将源容器中的部分元素賦給目标容器。這部分元素由源容器的兩個疊代器指定。
②void assign (size_type n, const value_type& val);
将n個相同的元素賦給目标容器,每個元素都是參數val的副本。value_type是容器中元素的類型。
③void assign (initializer_list<value_type> il);
使用初始化清單對容器指派。該方法将初始化清單il中的元素全部複制到目前容器中,替換已有的元素。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
vector<int> temp1 = { 0,1 };
temp1.assign(temp.begin() + 1, temp.end()); //temp1包含四個元素2,3,4,5
for (int i = 0; i < temp1.size(); i++)
cout << temp1[i] << endl ;
temp1.assign({ 1,2,3,4,5 }); //temp1包含五個元素1,2,3,4,5
for (int i = 0; i < temp1.size(); i++)
cout << temp1[i] << endl;
}
編譯運作結果:
十.傳回指針(通路元素)
value_type* data() noexcept;
const value_type* data() const noexcept;
vector中的元素除了可以使用疊代器和傳回引用的函數通路外,還可以使用指針來通路每一個元素,該指針由成員函數data()傳回。vector提供了data()函數的重載,調用時會根據容器内的元素是否是const類型來自動比對。
data()函數傳回指向容器内部使用的數組的第一個元素的指針,這表明調用該函數并不能直接通路元素,但是可以通過該指針來通路或修改元素。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp = { 1,2,3,4,5 };
int* ptr = temp.data();
for (int i = 0; i < temp.size(); i++)
{
cout << *(ptr + i) << endl;
}
}
編譯運作結果:
十一. 容器大小相關
前面多次提到了容器的大小,但是這些所說的“大小”并不是完全一樣的概念,有的表示容器目前存儲的元素的個數大小,有的表示容器的容量大小(所配置設定的記憶體空間的大小)。
1. 成員函數size()
size_type size() const noexcept;
size()函數傳回容器目前的元素個數。反映了調用該方法時容器一共存儲了多少個元素。
傳回類型size_type是一個無符号的整數類型
2. 成員函數capacity()
size_type capacity() const noexcept;
capacity()函數傳回為目前的容器配置設定的存儲空間的大小,也是以元素個數為機關。capacity()函數的傳回值是一個偏向容量的概念。
容器的基礎是數組,也就是說容器的底層是用數組來存儲資料的。容器内部會根據元素個數的增長,對這個底層數組的大小進行擴充。這時有這樣的疑惑,數組的大小在它聲明的時候不是已經确定了嗎,怎麼進行擴充?這裡的擴充是指建立一個新的更大的數組,然後将之前使用的數組中的内容複制到這個更大的數組中。
比如我們使用vs2019,通過向容器中push_back()更多的元素,然後檢視它的capacity,就會發現這樣一組資料: 42 63 94 141 211 316 474 …
也就是說,如果容器中目前的元素個數(size()的傳回值)沒有超過42,那麼為這個容器配置設定的存儲空間的容量就是42,即底層實作的數組的大小是42;如果向裡面再存入元素,使其元素個數超過42,但沒有達到63,那麼底層就會建立一個新的數組,數組的大小是63;以此類推……
注意:容器的底層實作是數組,容器的無限增長是通過不斷地建立更大的數組來替代目前使用的數組。當容器中存儲的元素的個數達到或超過目前容量時,容器會自動擴充(底層的數組),即重新配置設定存儲空間。這樣可以達到節省記憶體的目的。
3. 成員函數max_size()
size_type max_size() const noexcept;
max_size()函數傳回目前系統環境下,任意vector對象可以容納的元素的個數。這是一個理論上的容量的最大值,由系統和庫實作決定的,與程式無關,無論容器的元素個數怎麼增長,該函數的傳回值都不會變化。比如檢視本機的vector理論容量:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
cout << temp.max_size() << endl;
}
編譯運作結果:
4. 成員函數resize()
void resize (size_type n);
void resize (size_type n, const value_type& val);
resize()函數用來調整容器大小,使其包含n個元素。如果調用該方法時,容器包含的元素個數超過n,則會保留前n個元素,删除其他元素(并銷毀這些元素);如果容器包含的元素個數小于n,那麼會在容器後面持續插入元素直至元素個數達到n,以達到擴充容器的效果,這些插入的元素是由參數val決定的,如果指定了參數val,那麼這些元素會被初始化為val的副本,否則會被初始化為預設值。
另外,如果n超過了目前容器的容量,那麼容器會自動擴充(底層的數組),即重新配置設定存儲空間;而如果n小于目前容器的容量的話,容器并不會重新配置設定存儲空間。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
for (int i = 0; i < 111; i++)
temp.push_back(i);
cout << temp.capacity() << endl;
temp.resize(400); //超出目前容量,這裡會重新配置設定記憶體空間
cout << temp.capacity() << endl;
temp.resize(111); //不會重新配置設定記憶體空間
cout << temp.capacity() << endl;
}
編譯運作結果:
5. 成員函數reserve()
void reserve (size_type n);
人為設定容器的容量。reserve()函數會保證容器達到至少能容納n個元素的容量。如果n大于目前容器的容量,則調用該函數時,會為容器重新配置設定存儲空間,将其容量提升為n;其他情況下,調用該函數不會引起存儲空間的重新配置設定,容器目前的容量不受影響。
前面第2點capacity()函數中我們提到過,自動配置設定存儲空間時,容器的容量是一些離散的固定值,但是使用reserve()的時候,容量的容量可以是任意正整數。
注意:如果參數n大于容量的最大值(max_size()傳回),會引發length_error異常
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp;
for (int i = 0; i < 111; i++)
temp.push_back(i);
cout << temp.capacity() << endl;
temp.reserve(150);
cout << temp.capacity() << endl;
}
編譯運作結果:
6. 成員函數shrink_to_fit()
void shrink_to_fit();
shrink_to_fit()函數會調整容器容量以适應容器大小。有時候向容器裡寫入了太多的元素,導緻這個容器的容量自動增長得很大,并且為這個容器配置設定了很大的存儲空間。如果此時元素個數變為一個較小的值(比如使用resize()調整容器大小),可以調用shrink_to_fit()函數來擷取一個與此時的容器大小接近的容量,釋放多餘的暫時用不到的存儲空間。
示例:
#include<iostream>
#include<vector>
using namespace std;
int main()
{
vector<int> temp(1000, 6);
cout << temp.capacity() << endl;
temp.resize(100);
cout << temp.capacity() << endl;
temp.shrink_to_fit();
cout << temp.capacity() << endl;
}
編譯運作結果:
十二. 交換兩個容器内容
void swap (vector& x);
略... ...