---
GA: G-RZYLL0RZGV
---
###### tags: `大一程設-下` `東華大學` `東華大學資管系` `基本程式概念` `資管經驗分享`
vector,另一種比 array 還方便的陣列
===
[TOC]
## 前言
<span style="color:red">**參考文章請見最下面,這兩篇寫得非常好!!**</span>
上學期我們學過了最基礎的一般陣列,有特別強調說,他是在靜態時期編譯起來你就必須給他直接長度的,不能透過輸入賦予長度,雖然在某些 compiler 會過,但其實這件事情是不被大家所允許的。
接下來的 vector 就是動態的配置陣列大小給陣列,不用去操心記憶體,或是程式出錯,完全會自動配置大小,是非常好用的一個工具。
而 vector 是 array 的進階版,所以他還是一種 array,仍舊是連續配置記憶體,當容量不夠的時候會重新申請空間,並把原本的資料複製或搬到新的記憶體去。
但這樣有點不好,如果今天我們一直往 vector 裡面丟東西,一旦容量不夠,他可能會一直搬來搬去,其實有點沒有效率,因為他會一直釋放資源,添加資源,所以應該減少他配置新空間的次數。
上面這句話簡單的說就是,針對需求,可以配置比需求**多一倍**的空間,會是比較適當的。
## vector library 引入
要使用 vector,請務必要 `#include <vector>` 這個 Library 進來!
## vector 常用功能摘要
<span style="color:red;font-size:25px">**只是摘要,功能還有更多哦!**</span>
既然是陣列,他就是一種容器,空間就很重要,所以針對常用功能做摘要說明,下面會再細部的用例子帶大家理解。
* **Capacity,容量相關**
* `size()` 回傳陣列(vector)目前的長度
* `empty()` 看陣列是不是空的,如果 size 為 0 則回傳 true/1,反之則回傳 false/0
* `reserve()` 預先配置大小
* **Modifiers,修飾器,針對放資料進陣列或從陣列移除資料**
* `push_back()` 放資料到 vector 的最後面
* `pop_back()` 從最後面刪除一個資料
* `insert()` 插入資料進 vector
* `erase()` 從 vector 內移除資料
* `clear()` 清空 vector
* **Iterators,疊代器,用來把指針指到我們要的位置**
* `begin()` 把疊代器指向第一個元素
* `end()` 把疊代器指向最後一個元素的下一個位置
* [What is the past-the-end iterator in STL C++?](https://stackoverflow.com/questions/15252002/what-is-the-past-the-end-iterator-in-stl-c)
* **Element Access,存取元素**
* `at(i)` 回傳參數索引值所在位置的值
* `front()` 回傳 vector 頭的值
* `back()` 回傳 vector 尾的值
* `[i]` 與 at 的用法相似,取得索引值位置的值
## vector 宣告與初始化!
### Declaration 宣告
`vector<data_type> variable_name;`
```cpp=
vector<int> arr_name;
```
vector 代表你要宣告一個 vector,<>符號裡面放的是你這個陣列要儲存的資料的型態,即每一格要放的資料型態,arr_name 就是為你的 vector 自訂一個名字。
### Initialize 初始化
你可以看到上面這樣的宣告,好像沒有告訴說這個陣列應該要存多少個資料,因為前面有講,vector 可以自動處理大小,所以可以不用宣告大小,但我會覺得養成宣告大小的習慣會好一些!
```cpp=
vector<int> arr_name(10);
//一維陣列,有十個儲存空間,每個空間都必須存整數,值預設每一個都會存 0
```
### 宣告即附值與取大小
上面是只有宣告大小,那如果想要在宣告陣列即給值可以嗎? 當然可以!
```cpp=
vector<int> a({1,2,3,4,5,6,7,8});
//宣告一個名為 a 的 vector,其容量為 8
// a[0] = 1, a[1] = 2, a[2] = 3,...依此類推,大家都會
```
請注意,這樣寫就不用再宣告長度了,因為他就依據設置的值就可以知道容量多少,請記得 vector 可以動態配置大小。
今天這樣的一個 vector,可以怎麼取得長度呢?就是使用前面說的 size 成員函式。
```cpp=
vector<int> a({1,2,3,4,5,6,7,8});
cout << a.size(); // 當然會印出 8
```
在做值的初始化的時候,還有另一種寫法。
```cpp=
vector<int> a(5,100);
vector<string> b(10,"orange");
```
這個寫法是說,第一個參數代表陣列的 size,第二個參數是這個陣列一被宣告的時候,裡面的每一格初始化為什麼資料。所以 a 有五格,每一格都是 100。
但請注意,<span style="color:red">**不要自作主張的寫成以下這樣**</span>。
```cpp=
vector<int> a(5,100,60,7,8,13);
```
不要覺得說第一個數字既然是 size,那我後面是不是可以直接寫每一格的內容是甚麼,<span style="color:red">**沒有這種寫法,請千萬注意**</span>
**在下方的參考文章內還有複製 vector 的寫法,但不是我覺得重要的點,有興趣歡迎去參考文章看看!**
## 基本的插入/刪除內容進 vector
### push_back & pop_back
* push_back()
* 括號內的內容須與你 vector 的儲存資料的型態相同
* push_back() 是把資料放到 vector 的最後面
* pop_back()
* 括號內的內容須與你 vector 的儲存資料的型態相同
* pop_back() 是把資料從 vector 的最後面刪掉
```cpp=
#include <vector>
using namespace std;
int main(){
vector<int>a; // 宣告一個空的陣列,目前大小空間為 0
a.push_back(5); // a 的 size 為 1
a.push_back(7); // a 的 size 為 2
a.push_back(3); // a 的 size 為 3
vector<string>b;
b.push_back("orange"); // b 的 size 為 1
b.push_back("apple"); // b 的 size 為 2
b.push_back("banana"); // b 的 size 為 3
a.pop_back(); // 放在 a 最後面的此時是 3,所以 3 被刪除
a.size(); // 此時 a 的 size 為 2
a.pop_back(); // 放在 a 最後面的此時是 7,所以 7 被刪除
a.size(); // 此時 a 的 size 為 1
return 0;
}
```
但如果你想要初始化陣列的時候就給予大小,你要注意下面這樣的寫法。
```cpp=
int main(){
vector<int> a(4);
a.push_back(3); // 此時 a[4] 也就是第五格,資料為 3
a.push_back(5); // 此時 a[5] 也就是第六格,資料為 5
a.push_back(9); // 此時 a[6] 也就是第七格,資料為 9
}
```
你可能會納悶為甚麼,因為 vector 在宣告即給大小的時候,他就配置的大小為 4 格的陣列出來,每一格都是 0,所以我們此時若 push_back,他會再加一個元素進來,成為第五位成員。
所以不要傻傻地覺得宣告沒有設值他會乖乖地從 0 開始哦,vector 不是這樣運作的。
**上面的程式碼的話圖會像這樣。**

所以如果想要動前四個的內容,就是使用我們學過的中括號摟!
```
a[0] = 5;
a[1] = 7;
a[2] = 11;
a[3] = 1;
```
到這邊你可能會覺得,只能夠從最後面增加跟刪除,好像有一點點沒有效率耶。
對!所以 vector 還有更進階的能夠直接插入或刪除中間元素的方式,我們後面來談。
但在談他們之前,我們要先來理解一下 vector iterator 中的 begin() 跟 end()。
不過 iterator (疊代器) 是比較進階的 vector,所以如果你想挑戰,歡迎去旁邊挑戰一下 Boss -> [我是旁邊的 Boss](https://hackmd.io/@ndhu-programming-2021/rJlf9BTTY)
比較不行的同學,你可以搞懂下面就好。
## vector 奇妙混淆學習者的特性 - capacity
### vector 中 size 與 capacity 的差異
vector 的 capacity 是有必要好好談論的,他和 size 是完全不一樣的東西哦。
capacity 是取得目前 vector 配置的空間大小,而 size 是取得目前 vector 中有的元素個數。你可以想像成一個能夠裝 10 個東西的箱子(capacity),他目前裡面有 3 樣東西(size)。
而如果我們一直朝 vector 內放東西,放到滿了之後,因為 vector 會自動配置大小,所以他會擴充他的容量。而每次的擴充都會是當前 capacity 的 2 倍或 1.5 倍,取決於你的 compiler。
而容量會是 1、2、4、8、16、32,以 2 的冪次方成長下去。
```cpp=
int main(){
vector<double> a;
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
return 0;
}
```
所以上面的程式輸出如下,希望你能了解 capacity 跟 size 的差異。

## vector 中 reserve() 的用法
### reserve 預先配置容器
reserve 函式能夠讓你告訴程式 vector 的 capacity 要多大。但裡面沒有放任何東西,所以 size 會是 0。
```cpp=
int main(){
vector<int> a;
a.reserve(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity();
}
```

所以直到你 push_back 到他 size 要比 capacity 大的時候,他才會再擴充空間喔。照上面的說明來看,當我今天 size 要變成 11 的時候,capacity 就會變成 20 摟!
### 宣告初始化長度與 reserve 的不同
如果你今天是在宣告的時候就給予 vector 長度,像這樣 `vector<int> a(10)`。則你的 size 跟 capacity 都會是 10 哦,原因是因為上面這樣的宣告方式,會把全部的格子初始化成 0,所以現在裡面的 10 格都放了 0。
但上面的 reserve 只有設定容器大小,還沒有在裡面放東西。
```cpp=
int main(){
vector<int> a(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity();
}
```

希望大家不要搞混摟!
## vector shrink_to_fit() 容器收縮
今天既然有配置容器大小,那有沒有把多餘用不到的刪除這件事呢? 當然也是有的,就是依靠 shrink_to_fit() 這個成員函式。
```cpp=
int main(){
vector<int> a;
a.reserve(10);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
a.push_back(1);
a.shrink_to_fit();
cout << "size:" << a.size() << ", capacity:" << a.capacity() << endl;
}
```

有沒有發現他相較於陣列真的方便很多,雖然語法可能剛開始學會難一點點,但在一維陣列的處理上真的方便許多,雖然 vector 的二維寫起來比較困難一點就是了。
## vector 的 resize()
那今天有方法把多餘的刪除,那有沒有辦法在放了很多資料後,再把長度根據我們的需求改動呢? 一樣有的,就是依靠 resize() 成員函式。但他與 reserve 不同的是,多被配置出來的新空間,預設會補 0,至於你說 string、double 型態的 vector 預設會補上甚麼,大家可以自己測試。
```cpp=
int main(){
vector<int> a({1,2,3,4,5,6});
cout << "size:" << a.size() << "capacity:" << a.capacity() << endl;
//假設此時我們收到更進階的需求,知道需要 15 格。
a.resize(15);
cout << "size:" << a.size() << "capacity:" << a.capacity() << endl;
return 0;
}
```

vector 的基礎就先介紹到這邊啦,當然這些不是全部,但我相信也夠你們學了,有興趣的歡迎把 vector 比較進階的應用也學起來哦。
## vector 的優缺點!
### Advantages
* 宣告時不用確定大小,由後續的開發配置長度
* 節省空間,更提供 shrink_to_fit() 函式供開發者省去空間
* 支持 random access,也就是可以依靠 [i] 去取得資料
* 插入比較方便
* 若要針對一搬的陣列做插入,要整個搬移。
### Disadvantages
* 插入與刪除比較複雜,且效率比較低落
* 你可能要搞懂疊代器(iterator)
* 只能再末端 push 跟 pop,但正常來說開頭端也可以更好,像 queue 一樣。
## Reference : vector 超棒文章以及推薦!!
請照順序看!!
* [vector 基本介紹](https://shengyu7697.github.io/std-vector/)
* [vector 實作二維陣列](https://ramihaha.tw/c-program-container-vector-array-linklist/)
* [vector 的 C++ 官方文件](https://www.cplusplus.com/reference/vector/vector/)