C++ vector 是一個可以改變陣列大小的序列容器。C++ vector 是陣列的升級版,主要因為 vector 能高效地對記憶體進行管理以及動態增長。vector 其實就是將陣列和方法封裝形成的一個類別。

C++ 要使用 vector 容器的話,需要引入的標頭檔: <vector>

# vector 初始化

這樣是宣告一個 int 整數類型的 vector,裡面沒有任何元素 (空),size 為 0 表示 vector 容器中沒有任何元素,capacity 也是 0。

#include <vector>
vector<int> v;

# push_back()

先宣告一個空的 vector,再透過 push_back() 將資料一直推進去。

vector<int> v;
v.push_back(1);
v.push_back(2);

也可以寫成一行,但這語法需要編譯器 C++11 支援。

vector<int> v = {1, 2, 3};
// or
vector<int> v({1, 2, 3});

# 複製容器

假如要從另外一個 vector 容器複製資料過來當作初始值的話可以這樣寫。

vector<int> v1 = {1, 2, 3};
vector<int> v2 = v1;
// or
vector<int> v1 = {1, 2, 3};
vector<int> v2(v1);

也可以從傳統陣列裡複製過來當作初始值。

int n[3] = {1, 2, 3};
vector<int> v(n, n+3);

不想複製來源 vector 全部的資料,想要指定複製 vector 的範圍的話也可以,例如我要複製 v1 vector 的第三個元素到倒數第二個元素。

vector<int> v1 = {1, 2, 3, 4, 5};
vector<int> v2(v1.begin() + 2, v1.end() - 1);

如果是指定複製傳統陣列的範圍的話,可以這樣寫。

int n[5] = {1, 2, 3, 4, 5};
vector<int> v(n + 2, n + 4);

# 存取 vector 元素

vector 用 [] 來隨機存取元素,第一個元素為 v[0] ,索引值是 0 ,第二個元素為 v[1] ,索引值是 1 ,依此類推, [] 不只可以讀取元素也可以用來修改元素

vector<int> v = {1, 2, 3};
cout << "v[0] = " << v[0] << endl;
v[0] = 4;
cout << "v[0] = " << v[0] << endl;

v[0] = 1
v[0] = 4

# 在 vector 尾巴新增元素

要在 vector 尾巴新增元素,要使用前面提到的 push_back() ,它會把元素加在 vector 的尾巴。

vector<int> v;
v.push_back(0);

# 在 vector 尾巴移除元素

移除 vector 容器尾巴的元素用 pop_back()一次只能從尾端移除一個元素,不能指定移除的數量

vector<int> v = {1, 2, 3};
v.pop_back(); // [1, 2]

# 迴圈遍歷 vector

第一種是常見的用法,

vector<int> vec = {1, 2, 3};
for (int i=0; i<vec.size(); i++) {
    cout << vec[i] << ' ';
}

1 2 3

第二種是使用 iterator 迭代器來印出 vector 內所有內容,其中 vector<int>::iterator it 可以簡化寫成 auto it 這樣

vector<int> vec = {1, 2, 3};
vector<int>::iterator it;
for (it = vec.begin(); it != vec.end(); it++) cout << *it << ' ';
// or
for (auto it = vec.begin(); it != vec.end(); it++) cout << *it << ' ';

第三種是個很方便的寫法,c++11 才有支援,適合追求快速 (懶惰) 的人,相較於第一種的優點是不用多寫陣列索引去存取,直接就當變數使用。

vector<int> vec = {1, 2, 3};
for (auto &v: vec) cout << v << ' ';

# vector 使用 [] operator 與 at () 的差異

另外 vector 還提供了 at() 這個方法也是可以取得元素,那這兩種方式到底有什麼差別?

[] operator 在回傳元素時是不會作任何的邊界檢查,而在 at() 取得元素時會作邊界的處理,如果你存取越界時 vector 會拋出一個 out_of_range 例外,所以 at() 提供了較為安全的存取方式。

# vector size () 與 capacity () 的差異

vector 使用 size()取得目前 vector 裡的元素個數,vector 使用 capacity()取得目前 vector 裡的預先配置的空間大小,當容量 (capacity) 空間不夠使用時 vector 就會重新申請空間,容量 (capacity) 會增加為原來的 2 倍1.5 倍,例如:1、2、4、8、16、32 增長下去,各個編譯器可能不同。

# reserve () 預先配置容器大小

vector 使用 reserve() 是預留空間的意思,如果我們一開始就知道容器的裡要放置多少個元素的話,可以透過 reserve() 來預先配置容器大小,這樣可以減少一直配置記憶體的機會。

觀察看看 size 與 capacity 的變化。

vector<int> v;
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";
v.reserve(5);
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";
v.push_back(1);
v.push_back(2);
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";

size = 0, capacity = 0
size = 0, capacity = 5
size = 2, capacity = 5

# shrink_to_fit () 收縮的用法

呈上述 reserve 例子,這時 vector 再使用 shrink_to_fit() 成員函式的話,會釋放(free)那些尚未使用的空間

vector<int> v;
v.reserve(5);
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";
v.push_back(1);
v.push_back(2);
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";
v.shrink_to_fit();
cout << "size = " << v.size() << ", capacity = " << v.capacity() << "\n";

size = 0, capacity = 5
size = 2, capacity = 5
size = 2, capacity = 2

如果 size() 剛好等於 capacity() 的話,那麼使用 shrink_to_fit() 則不會有空間被釋放。

# resize()

vector 使用 resize()reserve() 不太一樣,resize 變大時會把多的元素補 0

vector<int> v;
v.resize(5);
cout << "size =" << v.size() << ", capacity =" << v.capacity() << "\n";
for (int i = 0; i < v.size(); i++) cout << v[i] << " ";

size = 5, capacity = 5
0 0 0 0 0

resize 如果要順便指定元素初始值的話,可以將初始值帶入 resize () 的第二個引數。

vector<int> v;
v.resize(5, 10);
cout << "size =" << v.size() << ", capacity =" << v.capacity() << "\n";
for (int i = 0; i < v.size(); i++) cout << v[i] << " ";

size = 5, capacity = 5
10 10 10 10 10

如果 resize 的大小超過 capacity 容量大小會怎麼樣呢?

merk:3
vector<int> v = {1, 2, 3};
cout << "size =" << v.size() << ", capacity =" << v.capacity() << "\n";
v.resize(5);
cout << "size =" << v.size() << ", capacity =" << v.capacity() << "\n";
for (int i = 0; i < v.size(); i++) cout << v[i] << " ";

輸出如下,原本的 1, 2, 3 元素有保留以外,剩下新增的元素補 0

size = 3, capacity = 3
size = 5, capacity = 6
1 2 3 0 0

# vector 的優缺點

# 優點

  • 宣告時可以不用確定大小
  • 節省空間
  • 支持隨機訪問 [i]

# 缺點

  • 進行插入刪除時效率低
  • 只能在末端進行 pop 和 push

# 參考資料

  • https://shengyu7697.github.io/std-vector/