走進 STL:快速導覽 C++ 標準模板庫的核心要素
STL 是什麼?
STL(Standard Template Library)
是 C++ 程式設計語言中一個非常重要和強大的函式庫,它提供了一系列資料結構,如向量(vector)、列表(list)和雜湊表(map),以及搜尋、排序等演算法和通用函式,可用於大幅簡化 C++ 程式開發。 STL 遵循泛型程式設計(Generic Programming) 的理念, 基於模板(Template) 來實作各種資料結構與演算法,這使得 STL 可以處理任何符合特定需求的資料類型,而不局限於特定的類型。這種泛型性增加了 STL 的可重用性和靈活性。
STL 的核心主要由以下幾大組建所組成:
- 容器 Container
- 演算法 Algorithms
- 迭代器 Iterator
- 仿函數 Functor
- 適配器 Adaptor
容器 Container
容器(Container)
是 STL 中用於存放資料的資料結構類別模板,每種容器都提供了特定資料結構的功能。
容器類型
STL 提供了以下幾種類型的容器:
- 序列容器(Sequence Containers)
- vector:表示可動態調整大小的陣列。支持快速隨機存取,但在中間插入或刪除元素可能會比較慢。
- deque:和
vector
相似,但設計上支持在頭部和尾部高效插入和刪除。 - list:雙向鏈表,支持在任何位置快速插入和刪除,但不支持快速隨機存取。
- forward_list:僅支持向前遍歷的鏈表,相比
list
更節省空間。
- 關聯容器(Associative Containers)
- 無序關聯容器(Unordered Associative Containers)
- unordered_set:基於哈希表的集合,元素唯一,不自動排序,提供快速查找。
- unordered_multiset:與
unordered_set
相似,但元素可以重複。 - unordered_map:基於哈希表的鍵-值對集合,提供快速查找。
- unordered_multimap:與
unordered_map
相似,但一個鍵可以關聯多個值。
容器操作方法
所有 STL 容器都提供了一些共通的操作,如:
- size():返回容器中元素的數量。
- empty():判斷容器是否為空。
- insert():插入元素到容器中。
- erase():刪除容器中的元素。
- clear():移除容器中所有元素。
如何使用容器
STL 容器的使用方式非常簡單。要使用 STL 容器,我們首先需要包含 STL 標頭檔。例如,要使用向量資料結構,我們需要包含以下標 頭檔:
#include <vector>
包含標頭檔後,我們就可以使用 STL 中提供的資料結構、演算法和函式。例如,以下程式碼使用向量資料結構來存放一組整數:
#include <vector>
int main() {
std::vector<int> numbers = {1, 5, 3, 4, 3};
// 迭代器
for (auto it = numbers.begin(); it != numbers.end(); ++it) {
std::cout << *it << " ";
}
// 演算法
std::sort(numbers.begin(), numbers.end());
// 函式
std::cout << std::count(numbers.begin(), numbers.end(), 3) << std::endl;
return 0;
}
// 1 5 3 4 3
// 1 3 3 4 5
// 2
迭代器 Iterator
迭代器(Iterator)
在 STL 中是一種可以用於訪問、遍歷和操作容器中元素的物件。迭代器是一種廣義的指針,大多數的時候,我們可以把它視為指向容器中元素的普通指針來使用,但它具有比普通指針更強大的功能。
以下整理迭代器的幾個特點:
- 可以指向容器中的元素。
- 可以遞增或遞減,以訪問序列中的相鄰元素。
- 可以比較兩個迭代器,以確定它們指向的元素的位置。
- 可以用於將各種 STL 算法套用在 STL 容器上,例如
std::sort
和std::find
。
標準容器迭代器的運算符
以下是一些常見的標準容器迭代器運算符:
*iter
: 解引用迭代器,獲取其指向的元素的值。iter->member
: 如果迭代器指向的是結構體或類型的物件,則可以使用此運算符訪問其成員。iter++ or ++iter
: 將迭代器遞增,指向序列中的下一個元素。iter-- or —-iter
: 將迭代器遞減,指向序列中的前一個元素(僅限雙向和隨機存取迭代器)。iter1 - iter2 or iter1 + iter2
: 對迭代器進行算術運算,返回距離(僅限隨機存取迭代器)。iter1 == iter2
: 比較兩個迭代器,確定它們是否指向相同的元素。iter1 != iter2
: 比較兩個迭代器,確定它們是否指向不同的元素。
迭代器類型
STL 提供了五種基本的迭代器類型:
迭代器類型 | 迭代器功能 | 範例 |
---|---|---|
輸入迭代器 Input iterator | 允許讀取序列中的元素,只能單向移動。 | istream |
輸出迭代器 Output iterator | 允許寫入序列中的元素,也只能單向移動。 | ostream, inserter |
前向迭代器 Forward iterator | 支持單向訪問和修改 | forward_list |
雙向迭代器 Bidirectional iterator | 除了前向迭代器的功能外,還可以向後移動。 | list, set, multiset, map, multimap |
隨機迭代器 Random access iterator | 提供直接訪問任意元素的能力,功能最強。 | vector, deque, array, string |
其他迭代器
除了基本的迭代器類型外,STL 也提供了一些特殊用途的迭代器:
迭代器類型 | 迭代器功能 | 範例 |
---|---|---|
逆向迭代器 Reverse iterator | 允許以相反的順序遍歷容器。 | vector, list, deque |
插入迭代器 Insert iterator | 專為在容器中插入元素而設計,不進行元素訪問。 | inserter, back_inserter, front_inserter |
流迭代器 Stream iterator | 用於從流中讀寫數據。 | istream_iterator, ostream_iterator |
移動迭代器 Move iterator | 允許元素的移動而非複製,用於優化性能。 | make_move_iterator |
使用範例
下面提供一個使用迭代器的範例遍歷與操作容器元素的範例:
#include <vector>
#include <list>
#include <iostream>
#include <iterator> // 為了使用 inserter 和 fill_n
using namespace std;
int main() {
// 遍歷vector
vector<int> v = {1, 2, 3, 4, 5};
for(auto it = v.begin(); it != v.end(); ++it) {
cout << *it << " "; // 輸出: 1 2 3 4 5
}
cout << endl;
// 反向遍歷
for(auto rit = v.rbegin(); rit != v.rend(); ++rit) {
cout << *rit << " "; // 輸出: 5 4 3 2 1
}
cout << endl;
// 修改list中的元素
list<string> lst = {"apple", "banana", "orange"};
auto it = lst.begin();
*it = "pear"; // lst變為 {"pear", "banana", "orange"}
// 使用inserter插入元素
vector<int> v2;
fill_n(inserter(v2, v2.begin()), 5, 100); // v2變為{100, 100, 100, 100, 100}
return 0;
}
演算法 Algorithms
STL 提供了豐富的演算法函數,能夠在各種容器上執行不同的操作。這些演算法函數定義在 <algorithm>
和 <numeric>
標頭檔案中。
根據功能,演算法大致上可以分為以下幾類:
非變動序列操作
這些演算法不會修改操作對象,通常用於查詢或條件測試。
演算法 | 功能 | 範 例 | 時間複雜度 |
---|---|---|---|
all_of | 檢查範圍內的所有元素是否都滿足某個條件 | all_of(v.begin(), v.end(), pred) | O(n) |
any_of | 檢查範圍內是否至少有一個元素滿足某個條件 | any_of(v.begin(), v.end(), pred) | O(n) |
none_of | 檢查範圍內是否沒有元素滿足某個條件 | none_of(v.begin(), v.end(), pred) | O(n) |
find | 在範圍內尋找第一個等於特定值的元素 | find(v.begin(), v.end(), value) | O(n) |
find_if | 在範圍內尋找第一個滿足條件的元素 | find_if(v.begin(), v.end(), pred) | O(n) |
count | 計算範圍內等於特定值的元素數量 | count(v.begin(), v.end(), value) | O(n) |
count_if | 計算範圍內滿足某個條件的元素數量 | count_if(v.begin(), v.end(), pred) | O(n) |
equal | 檢查兩個範圍內的元素是否全部相等 | equal(v1.begin(), v1.end(), v2.begin()) | O(n) |
search | 在範圍內尋找第一次出現的另一個範圍的序列 | search(v.begin(), v.end(), subv.begin(), subv.end()) | O(n*m) |
變動序列操作
這些演算法會修改操作對象。
演算法 | 功能 | 範例 | 時間複雜度 |
---|---|---|---|
copy | 複製範圍內的元素到另一範圍 | copy(v.begin(), v.end(), dest.begin()) | O(n) |
copy_n | 複製指定數量的元素到另一範圍 | copy_n(v.begin(), n, dest.begin()) | O(n) |
copy_if | 根據條件複製元素到另一範圍 | copy_if(v.begin(), v.end(), dest.begin(), pred) | O(n) |
copy_backward | 反向複製元素到另一範圍 | copy_backward(v.begin(), v.end(), dest.end()) | O(n) |
move | 移動範圍內的元素到另一範圍 | move(v.begin(), v.end(), dest.begin()) | O(n) |
move_backward |