C++ vector,STL vector(可變長的動態陣列)詳解

2020-07-16 10:04:19
vector 是順序容器的一種。vector 是可變長的動態陣列,支援隨機存取疊代器,所有 STL 演算法都能對 vector 進行操作。要使用 vector,需要包含標頭檔案 vector。

在 vector 容器中,根據下標隨機存取某個元素的時間是常數,在尾部新增一個元素的時間大多數情況下也是常數,總體來說速度很快。

在中間插入或刪除元素時,因為要移動多個元素,因此速度較慢,平均花費的時間和容器中的元素個數成正比。

在 vector 容器中,用一個動態分配的陣列來存放元素,因此根據下標存取某個元素的時間是固定的,與元素個數無關。

vector 容器在實現時,動態分配的儲存空間一般都大於存放元素所需的空間。例如,哪怕容器中只有一個元素,也會分配 32 個元素的儲存空間。這樣做的好處是,在尾部新增一個新元素時不必重新分配空間,直接將新元素寫入適當位置即可。在這種情況下,新增新元素的時間也是常數。

但是,如果不斷新增新元素,多出來的空間就會用完,此時再新增新元素,就不得不重新分配記憶體空間,把原有內容複製過去後再新增新的元素。碰到這種情況,新增新元素所花的時間就不是常數,而是和陣列中的元素個數成正比。

至於在中間插入或刪除元素,必然涉及元素的移動,因此時間不是固定的,而是和元素個數有關。

vector 有很多成員函數,常用的如表 1 所示。

表1:vector中常用的成員函數
成員函數 作 用
vector() 無參建構函式,將容器初始化為空
vector(int n) 將容器初始化為有 n 個元素
vector(int n, const T & val) 假定元素的型別是 T,此建構函式將容器初始化為有 n 個元素,每 個元素的值都是 val
vector(iterator first, iterator last) first 和 last 可以是其他容器的疊代器。一般來說,本建構函式初始化的結果就是將 vector 容器的內容變成與其他容器上的區間 [first, last) —致
void clear() 刪除所有元素
bool empty() 判斷容器是否為空
void pop_back() 刪除容器末尾的元素
void push_back( const T & val) 將 val 新增到容器末尾
int size() 返回容器中元素的個數
T & front() 返回容器中第一個元素的參照
T & back() 返回容器中最後一個元素的參照
iterator insert(iterator i, const T & val) 將 val 插入疊代器 i 指向的位置,返回 i
iterator insert( iterator i, iterator first, iterator last) 將其他容器上的區間 [first, last) 中的元素插入疊代器 i 指向的位置
iterator erase(iterator i) 刪除疊代器 i 指向的元素,返回值是被刪元素後面的元素的疊代器
iterator erase(iterator first, iterator last) 刪除容器中的區間 [first, last)
void swap( vector <T> & v) 將容器自身的內容和另一個同型別的容器 v 互換

下面的程式演示了 vector 的基本用法。
#include <iostream>
#include <vector>  //使用vector需要包含此標頭檔案
using namespace std;
template <class T>
void PrintVector(const vector <T> & v)
{  //用於輸出vector容器的全部元素的函數模板
    typename vector <T>::const_iterator i;
    //typename 用來說明 vector <T>::const_iterator 是一個型別,在 Visual Studio 中不寫也可以
    for (i = v.begin(); i != v.end(); ++i)
        cout << *i << " ";
    cout << endl;
}
int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    vector <int> v(a, a + 5);  //將陣列a的內容放入v
    cout << "1) " << v.end() - v.begin() << endl;  //兩個隨機疊代器可以相減,輸出:1)5
    cout << "2)"; PrintVector(v);  //輸出:2)1 2 3 4 5
    v.insert(v.begin() + 2, 13);  //在 begin()+2 位置插人 13
    cout << "3)"; PrintVector(v);  //輸出:3)1 2 13 3 4 5
    v.erase(v.begin() + 2);  //刪除位於 begin()+2 位置的元素
    cout << "4)"; PrintVector(v);  //輸出:4)1 2 3 4 5
    vector<int> v2(4, 100);  //v2 有 4 個元素,都是 100
    v2.insert(v2.begin(), v.begin() + 1, v.begin() + 3);  //將v的一段插入v2開頭
    cout << "5)v2:"; PrintVector(v2);  //輸出:5)v2:2 3 100 100 100 100
    v.erase(v.begin() + 1, v.begin() + 3);  //刪除 v 上的一個區間,即 [2,3)
    cout << "6)"; PrintVector(v);  //輸出:6)1 4 5
    return 0;
}
思考題:程式中的 PrintVector 模板演示了將容器的參照作為函數引數的用法。就完成輸出整個容器內容這個功能來說,寫成 PrintVector 模板這樣是比較笨拙的,該模板的適用範圍太窄。有沒有更好的寫法?

vector 還可以巢狀以形成可變長的二維陣列。例如:
#include <iostream>
#include <vector>
using namespace std;
int main()
{   
    vector<vector<int> > v(3); //v有3個元素,每個元素都是vector<int> 容器
    for(int i = 0;i < v.size(); ++i)
        for(int j = 0; j < 4; ++j)
            v[i].push_back(j);
    for(int i = 0;i < v.size(); ++i) {
        for(int j = 0; j < v[i].size(); ++j)
            cout << v[i][j] << " ";
        cout << endl;
    }
    return 0;
}
程式的輸出結果是:
0 1 2 3
0 1 2 3
0 1 2 3

vector< vector<int> > v(3);定義了一個 vector 容器,該容器中的每個元素都是一個 vector <int> 容器。即可以認為,v 是一個二維陣列,一共 3 行,每行都是一個可變長的一維陣列。

在 Dev C++ 中,上面寫法中 int 後面的兩個>之間需要有空格,否則有的編譯器會把它們當作>>運算子,編譯會出錯。