【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)