【C++ 筆記】vector - part 16
===
目錄(Table of Contents):
[TOC]
---
很感謝你點進來這篇文章。
你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧!
簡介(Introduction)
---
在 C++ 中,vector 就像可調整大小的陣列 array。
vector 和 array 都是用於儲存相同資料型態的多元素資料結構(Data structure)。
兩者之間最大的差別,就是:
:::success
vector:可調整大小。
array:不可調整大小,初始化時就固定了。
:::
其中 vector 最重要的就是能夠自動管理記憶體使用,不必我們手動分配、釋放記憶體。
根據菜鳥教程,將 vector 列出了以下四點基本特性:
1. 動態大小:vector 的大小可以根據需要而自動增長和縮小。
2. 連續存儲:vector 中的元素在記憶體中是連續儲存的,這使得存取元素非常快速。
3. 可迭代:vector 可以被迭代,所以可用迴圈(如 for 迴圈)來存取它的元素。
4. 元素型態:vector 可以儲存任何型態的元素,包括內建類型、物件、指標等。
來源:https://www.runoob.com/cplusplus/cpp-vector.html
vector 標頭檔(vector's header file)
---
要使用 vector,需要做以下的引入:
```cpp=
#include <vector>
```
vector 的基本運算
---
### 建立 vector(Create a vector)
```cpp=
std::vector<int> my_vector; // 表建立一個儲存整數的空 vector
```
or
```cpp=
std::vector<int> my_vector(5); // 表建立可容納五個整數的 vector, 每個元素值預設是 0
std::vector<int> my_vector(5, 10); // 同上,但表示每個元素值都是 10
```
or
```cpp=
std::vector<int> my_vector = {1,2,3,4,5}; // 初始化包含元素的 vector
```
---
### 新增元素(Add elements)
使用 `(vector容器名稱).push_back(欲增加的元素)` 新增,如下:
```
my_vector.push_back(1); // 新增整數 1 至 my_vector 當中
```
---
### 存取元素(Access a vector)
可用註標運算子 `[]` 或 `(vector容器名稱).at(欲存取元素索引)` 存取,如下:
```cpp=
int x = myVector[0]; // 獲取第一個元素
int y = myVector.at(1); // 獲取第二個元素
```
至於兩者區別,筆者引用以下文章做解釋:https://shengyu7697.github.io/std-vector/
> `[]` operator 在回傳元素時是不會作任何的邊界檢查,而在 `at()` 取得元素時會作邊界的處理,如果你存取越界時 `std::vector` 會拋出一個 `out_of_range` 例外,所以 `at()` 提供了較為安全的存取方式。
---
### 修改元素(Change vector elements)
若要變更特定元素的值,如同陣列一般:
```cpp=
vector<string> cars = {"Volvo", "BMW", "Ford", "Mazda"};
// 改變第一個元素的值
// Change the value of the first element
cars[0] = "Opel";
// 現在輸出由 Opel 改成 Volvo
cout << cars[0]; // Now outputs Opel instead of Volvo
```
Source:https://www.w3schools.com/cpp/cpp_vectors.asp
---
### 迭代存取(Iterative access)
```cpp=
for (int element : myVector) {
std::cout << element << " ";
}
```
以上這種使用 `for ( for-range-declaration : expression )` 的寫法稱為「以範圍為基礎的 for 語句」(Range-based for Statement)。
:::success
此種 for 語句又可稱為 for-each loop。(要注意 C++ 11 才有支援)
:::
`for-range-declaration`:範圍宣告。於上例中,element 變數會在每次迭代(意即每次的 for 迴圈)當中,從 myVector 當中存取一個整數,並且指定(=)給 element。
`expression`:範圍之運算式,表示要迭代的容器,如:vector。
---
### 刪除元素(Delete elements)
使用 `erase()` 方法刪除,如下:
```cpp=
myVector.erase(myVector.begin() + 2); // 刪除第三個元素
```
為何寫 `vector.begin() + 2` 而不是直接寫 `2` 呢?
在 C++ 中,`vector.begin()` 是一個內建方法(built-in method),用於取得指向 vector 開頭的迭代器(iterator)。此迭代器用於遍歷(traverse)vector 或從 vector 的開頭開始執行操作。
簡單來說,`vector` 提供了迭代器來存取元素,`begin()` 方法回傳一個指向 `vector` 第一個元素的迭代器,相反地,`end()` 方法就是回傳一個指向最後一個元素之後位置的迭代器。
迭代器本身是一種資料型態,其意義是記憶體空間的位址,若要取得其值,則使用提取運算子(Dereference):`*(v.begin())`
那至於「為何寫 `vector.begin() + 2` 而不是直接寫 `2` 呢?」這個問題,其實就是利用`begin()` 回傳的迭代器進行偏移,可以取得指向特定元素的迭代器。
由於我們沒有要取值,只是要用 `erase()` 方法消除,所以直接寫 `myVector.begin() + 2` 即可。
有關迭代器的概念,由於解釋會較為複雜,所以留在後續章節~
---
### 清空 vector(Clear all elements)
使用 `clear()` 方法清除 Vector 中所有元素:
```cpp=
myVector.clear(); // 清空 vector
```
vector 方法整理
---
| 序號 | 方法 | 敘述 |
| -------- | -------- | -------- |
| 1 | `vector.push_back( <type> value )` | 將元素(element)加入 vector 的末尾 |
| 2 | `vector.at(size_t position)` | 回傳 vector 中的索引元素 |
| 3 | `vector.erase(iterator position)` or `vector.erase(iterator start, iterator end)` | 從 vector 中刪除多個元素 |
| 4 | `vector.size()` | 回傳 vector 中的元素數量(即回傳 vector 長度) |
| 5 | `vector.pop_back()` | 刪除 vector 的最後一個元素 |
| 6 | `vector.empty()` | 檢查 vector 是否為空 |
註:`3` 中 erase 方法具有兩種形式,一種為迭代器所在位置,另一種為迭代器的起始點至結束(範圍刪除)。
更多資訊可至:https://www.w3schools.com/cpp/cpp_ref_vector.asp
vector 範例
---
1. 從建立、新增、移除元素至輸出等完整 vector 範例
```cpp=
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main() {
// 建立 vector
vector<string> names; // 建立一個儲存字串的 vector
// 新增元素
names.push_back("Alice"); // 增加元素 "Alice"
names.push_back("Bob"); // 增加元素 "Bob"
names.push_back("Charlie"); // 增加元素 "Charlie"
// 輸出目前 vector 的大小
cout << "目前 vector 的大小: " << names.size() << endl; // 輸出 3
// 存取元素
cout << "第一個元素: " << names.at(0) << endl; // at() 存取第一個元素
cout << "第二個元素: " << names[1] << endl; // 索引存取第二個元素
// 輸出所有元素
cout << "所有名稱: ";
for (const auto& name : names) {
cout << name << " "; // 使用範圍 for 迴圈輸出每個名稱
}
cout << endl;
// 移除最後一個元素
names.pop_back(); // 移除最後一個元素 ("Charlie")
// 輸出移除後的大小
cout << "移除後的 vector 大小: " << names.size() << endl; // 輸出 2
return 0;
}
```
輸出結果:
```
目前 vector 的大小: 3
第一個元素: Alice
第二個元素: Bob
所有名稱: Alice Bob Charlie
移除後的 vector 大小: 2
```
2. 建立 Vector(基礎):https://www.w3schools.com/cpp/cpp_vectors.asp
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int main(){
// 建立一個 vector 叫做 cars, 會以字串形式儲存
// Create a vector called cars that will store strings
vector<string> cars = {"Volvo", "BMW", "Ford", "Mazda"};
// 印出 vector 元素
// Print vector elements
for (string car : cars) {
cout << car << "\n";
}
}
```
輸出結果:
```
Volvo
BMW
Ford
Mazda
```
3. `[]`(註標運算子) 存取 Vector 元素:https://www.w3schools.com/cpp/cpp_vectors.asp
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int main(){
// 建立一個 vector 叫做 cars, 會以字串形式儲存
// Create a vector called cars that will store strings
vector<string> cars = {"Volvo", "BMW", "Ford", "Mazda"};
// 取得第一個元素
// Get the first element
cout << cars[0] << endl; // Outputs Volvo : 輸出 Volvo
// 取得第二個元素
// Get the second element
cout << cars[1]; // Outputs BMW : 輸出 BMW
}
```
輸出結果:
```
Volvo
BMW
```
4. 顯示 0 ~ 9 的數字:https://openhome.cc/Gossip/CppGossip/vector1.html
```cpp=
#include <iostream>
#include <vector>
using namespace std;
int main() {
vector<int> number;
for(int i = 0; i < 10; i++) {
number.push_back(i);
}
while(!number.empty()) {
int n = number.back();
number.pop_back();
cout << n << endl;
}
return 0;
}
```
輸出結果:
```
9
8
7
6
5
4
3
2
1
0
```
總結
---
1. **基本介紹**
* **vector** 是一種可調整大小的動態陣列,與靜態陣列 array 不同。
* **特性**:
* 動態大小:根據需求而調整容量。
* 連續儲存:元素在記憶體中是連續排列,方便快速存取。
* 可迭代:支援用迭代器及範圍 for 迴圈存取。
* 多種型態支援:能存放基本型態、物件與指標等多種型態。
2. **使用方式**
* `#include <vector>` -> 使用前請先引入函式庫
* **建立 vector**:
```cpp=
std::vector<int> my_vector; // 空 vector
std::vector<int> my_vector(5); // 含 5 元素,初值 0
std::vector<int> my_vector(5, 10); // 含 5 元素,初值 10
std::vector<int> my_vector = {1, 2, 3}; // 初始化指定值
```
* **新增元素**:`push_back(value)` 新增元素至尾部。
* **存取元素**:
* `[]`:快速存取,無邊界檢查。
* `at()`:安全存取,越界會拋出例外。
* **修改元素**:同陣列修改方式,使用索引及註標運算子 `[]`。
* **迭代存取**:
```cpp=
for (int element : my_vector) {
std::cout << element << " ";
}
```
* **刪除元素**:
* 單一位置:`erase(iterator)`。
* 範圍刪除:`erase(iterator_start, iterator_end)`。
* **清空 vector**:
* `clear()`。
3. **常用方法表**
| 序號 | 方法 | 敘述 |
| -------- | -------- | -------- |
| 1 | `vector.push_back( <type> value )` | 將元素(element)加入 vector 的末尾 |
| 2 | `vector.at(size_t position)` | 回傳 vector 中的索引元素 |
| 3 | `vector.erase(iterator position)` or `vector.erase(iterator start, iterator end)` | 從 vector 中刪除多個元素 |
| 4 | `vector.size()` | 回傳 vector 中的元素數量(即回傳 vector 長度) |
| 5 | `vector.pop_back()` | 刪除 vector 的最後一個元素 |
| 6 | `vector.empty()` | 檢查 vector 是否為空 |
註:`3` 中 erase 方法具有兩種形式,一種為迭代器所在位置,另一種為迭代器的起始點至結束(範圍刪除)。
更多資訊可至:https://www.w3schools.com/cpp/cpp_ref_vector.asp
4. **優點及注意事項**
* 優點:
* 自動管理記憶體,免手動設定與釋放。
* 提供多樣的運算方法與高效能。
* 注意事項:
* 使用 `[]` 時需注意邊界,建議用 `at()` 提高安全性。
* 大量運算時可能因頻繁調整大小而影響效能。
參考資料
---
[使用 vector](https://openhome.cc/Gossip/CppGossip/vector1.html)
[C++ vector Library Reference (vector functions)](https://www.w3schools.com/cpp/cpp_ref_vector.asp)
[以範圍為基礎的 for 陳述式 (C++) | Microsoft Learn](https://learn.microsoft.com/zh-tw/cpp/cpp/range-based-for-statement-cpp?view=msvc-170)
[註標運算子:[] | Microsoft Learn](https://learn.microsoft.com/zh-tw/cpp/cpp/subscript-operator?view=msvc-170)
[C++ Vectors](https://www.w3schools.com/cpp/cpp_vectors.asp)
[Vector erase() in C++ STL - GeeksforGeeks](https://www.geeksforgeeks.org/vector-erase-in-cpp-stl/)
[C++ std::vector 用法與範例 | ShengYu Talk](https://shengyu7697.github.io/std-vector/)
[資料結構筆記:C++中的迭代器 iterator - hifone2191的創作 - 巴哈姆特](https://home.gamer.com.tw/artwork.php?sn=5818915)
[C++ vector 容器 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-vector.html)