C++ STL 教程


希望您已經理解了我們之前討論過的 C++ 模板的概念。C++ STL(標準模板庫)是一套強大的 C++ 模板類,它使用模板提供通用類和函式,實現了許多流行且常用的演算法和資料結構,例如向量、列表、佇列和棧。

C++ 標準模板庫的核心是以下三個結構良好的元件:

序號 元件及描述
1

容器

容器用於管理特定型別的物件的集合。有幾種不同型別的容器,例如 deque、list、vector、map 等。

2

演算法

演算法作用於容器。它們提供了執行容器內容初始化、排序、搜尋和轉換的方法。

3

迭代器

迭代器用於遍歷物件集合的元素。這些集合可能是容器或容器的子集。

我們將在下一章討論 C++ 標準庫時討論所有三個 C++ STL 元件。現在,請記住所有三個元件都有一套豐富的預定義函式,這些函式可以幫助我們以非常簡單的方式完成複雜的任務。

示例

讓我們來看以下程式,它演示了 vector 容器(一個 C++ 標準模板),它類似於陣列,區別在於它在增長時會自動處理自己的儲存需求:

#include <iostream>
#include <vector>
using namespace std;
 
int main() {

   // create a vector to store int
   vector<int> vec; 
   int i;

   // display the original size of vec
   cout << "vector size = " << vec.size() << endl;

   // push 5 values into the vector
   for(i = 0; i < 5; i++) {
      vec.push_back(i);
   }

   // display extended size of vec
   cout << "extended vector size = " << vec.size() << endl;

   // access 5 values from the vector
   for(i = 0; i < 5; i++) {
      cout << "value of vec [" << i << "] = " << vec[i] << endl;
   }

   // use iterator to access the values
   vector<int>::iterator v = vec.begin();
   while( v != vec.end()) {
      cout << "value of v = " << *v << endl;
      v++;
   }

   return 0;
}

當以上程式碼編譯並執行時,會產生以下結果:

vector size = 0
extended vector size = 5
value of vec [0] = 0
value of vec [1] = 1
value of vec [2] = 2
value of vec [3] = 3
value of vec [4] = 4
value of v = 0
value of v = 1
value of v = 2
value of v = 3
value of v = 4

以下是一些關於我們在上述示例中使用的各種函式需要注意的事項:

  • push_back() 成員函式在向量的末尾插入值,根據需要擴充套件其大小。
  • size() 函式顯示向量的尺寸。
  • begin() 函式返回指向向量開頭的迭代器。
  • end() 函式返回指向向量末尾的迭代器。

C++ 標準模板庫元件

C++ 標準模板庫的核心是以下四個結構良好的元件:

  • 容器
  • 演算法
  • 迭代器
  • 仿函式(函式物件)

容器

容器是用於儲存和管理資料或物件集合(例如向量、列表、集合和對映)的資料結構。在這裡,我們將看到幾種型別的容器。

1. 順序容器

它們是按特定線性順序儲存元素的容器。它們允許直接訪問元素以及管理元素順序和排列的功能。

  • 陣列 − 這些是固定大小的元素集合。
  • 向量 − 它是一個動態陣列,可以根據需要調整其大小。
  • 雙端佇列 − 雙端佇列,允許在兩端快速插入和刪除。
  • 列表 − 一個雙向連結串列,允許雙向迭代。
  • 前向列表 − 它是一個單向連結串列,允許高效插入和刪除,但只能單向遍歷。
  • 字串 − 在 C++ 中,字串也被認為是順序容器,它被實現為字元的動態陣列,其行為類似於其他順序容器。

2. 關聯容器

它以排序的順序儲存元素,允許根據鍵快速檢索。這些容器以鍵值對結構工作,其中每個元素都與一個唯一的鍵關聯。此鍵用於快速訪問相應的鍵值。

  • 集合 − 它是一個按特定順序排序的唯一元素的集合。
  • 對映 − 它是一組鍵值對,其中鍵是唯一的。
  • 多重集 − 它類似於集合,但允許重複元素。
  • 多重對映 − 它類似於對映,但允許重複鍵。

3. 無序關聯容器

它以無序的方式儲存元素,允許根據鍵高效訪問、插入和刪除。它們不維護元素的排序順序,而是使用雜湊來組織資料。

  • 無序集合 − 它是一組唯一的元素,沒有任何特定順序。
  • 無序對映 − 它是一組鍵值對,沒有任何特定順序。
  • 無序多重集 − 它允許重複元素,沒有任何特定順序。
  • 無序多重對映 − 它允許重複鍵,沒有任何特定順序。

4. 容器介面卡

它為現有容器提供不同的介面。

  • − 它是一種遵循後進先出 (LIFO) 原則的資料結構。
  • 佇列 − 它是一種遵循先進先出 (FIFO) 原則的資料結構。
  • 優先順序佇列 − 它是一種特殊的佇列,其中元素根據優先順序刪除。

演算法

C++ 標準模板庫 (STL) 中的演算法是一個大型函式集合,專門設計用於對容器執行操作。其中,這些演算法使用迭代器來遍歷容器,而無需瞭解其內部結構。

非修改順序演算法

  • for_each − 它應用於查詢範圍內的每個元素的函式。
  • count − 它計算範圍內某個值的出現次數。
  • find() − 它查詢範圍內某個值的第一次出現。
  • find_if − 它查詢滿足謂詞的第一個元素。
  • find_if_not − 它查詢不滿足謂詞的第一個元素。
  • equal − 它檢查兩個範圍是否相等。
  • search − 它在一個序列中搜索子序列。

1. 修改順序演算法

  • copy() − 它將元素從一個範圍複製到另一個範圍。
  • copy_if − 它將滿足謂詞的元素複製到另一個範圍。
  • copy_n − 它將特定數量的元素從一個範圍複製到另一個範圍。
  • move − 它將元素從一個範圍移動到另一個範圍。
  • transform() − 它將函式應用於範圍並存儲結果。
  • remove − 它從範圍內刪除具有特定值的元素。
  • remove_if − 它刪除滿足謂詞的元素。
  • unique − 它刪除連續的重複元素。
  • reverse() − 它反轉範圍內元素的順序。
  • swap() − 它交換元素。

2. 排序演算法

  • sort − 它對範圍內的元素進行排序。
  • stable_sort − 它對元素進行排序,同時保持等效元素的相對順序。
  • partial_sort − 它對範圍的一部分進行排序。
  • nth_element − 它對範圍進行分割槽,以便第 n 個元素位於其最終位置。

3. 搜尋演算法

  • binary_search − 它檢查元素是否存在於排序範圍內。
  • lower_bound − 它搜尋可以插入元素以保持順序的第一個位置。
  • upper_bound − 它搜尋大於指定值的第一個元素的位置。
  • binary_search − 它檢查元素是否存在於排序範圍內。
  • lower_bound − 它查詢可以插入元素以保持順序的第一個位置。
  • upper_bound − 它搜尋大於指定值的第一個元素的位置。
  • equal_range − 它返回相等元素的範圍。

4. 堆演算法

  • make_heap − 從一個區間建立堆。
  • push_heap − 向堆中新增一個元素。
  • pop_heap − 從堆中刪除最大元素。
  • sort_heap − 對堆中的元素進行排序。

5. 集合演算法

  • set_union − 計算兩個集合的並集。
  • set_intersection − 計算兩個集合的交集。
  • set_difference − 計算兩個集合的差集。
  • set_symmetric_difference − 計算兩個集合的對稱差集。

6. 數值演算法

  • accumulate − 計算一個區間的和(或其他操作)。
  • inner_product − 計算兩個區間的內積。
  • adjacent_difference − 計算相鄰元素之間的差。
  • Partial_sum − 計算一個區間的部分和。

7. 其他演算法

  • generate − 使用函式生成的值填充一個區間。
  • shuffle − 隨機打亂一個區間內的元素。
  • partition − 根據謂詞將元素劃分為兩組。

迭代器

C++ 標準模板庫 (STL) 中的迭代器是充當容器內元素指標的物件,它提供了一個統一的介面來訪問和操作資料。它們充當演算法和容器之間的橋樑。

  • 輸入迭代器 − 允許只讀訪問元素。
  • 輸出迭代器 − 允許只寫訪問元素。
  • 前向迭代器 − 允許讀寫,可以遞增。
  • 雙向迭代器 − 可以遞增和遞減。
  • 隨機訪問迭代器 − 支援算術運算,可以直接訪問元素。

函式物件(仿函式)

C++ 中的仿函式(或函式物件)是可以像函式一樣呼叫的物件。它可以像普通函式一樣被呼叫。此功能是透過過載函式呼叫運算子()來實現的。

在這裡,您將根據它們執行的操作探索不同型別的仿函式。

1. 算術仿函式

在 C++ 中,算術運算子用於執行基本的數學運算。以下是常見的算術運算子以及示例。

  • 加法 (+) − 將兩個值組合起來以產生它們的和。
  • 減法 (-) − 計算兩個值之間的差。
  • 乘法 (*) − 將兩個值相乘以產生它們的積。
  • 除法 (/) − 將一個值除以另一個值,得到商。
  • 模 (%) − 返回一個值除以另一個值的餘數。
  • 取反 (-) − 返回引數的取反值。

2. 比較仿函式

比較仿函式用於比較值,尤其是在容器中進行排序或搜尋時。

  • 小於 (<) − 比較並返回第一個值是否小於第二個值。
  • 大於 (>) − 比較並返回第一個值是否大於第二個值。
  • 小於等於 (≤) − 比較並返回第一個值是否小於等於第二個值。
  • 大於等於 (≥) − 比較並返回第一個值是否大於等於第二個值。
  • 等於 (=) − 比較並返回兩個值是否相等。
  • 不等於 (≠) − 比較並返回兩個值是否不相等。

3. 邏輯仿函式

邏輯仿函式執行邏輯運算,在涉及布林邏輯的場景中可能很有用。

  • 邏輯與仿函式 (&&) − 如果兩個布林引數中至少有一個或兩個為假,則返回假,否則返回真。
  • 邏輯或仿函式 (||) − 如果兩個布林引數中至少有一個或兩個為真,則返回真。
  • 邏輯非仿函式 (!) − 如果布林引數為假則返回真,反之亦然,它返回提供布林值的相反值。

4. 位運算仿函式

  • bit_and − 對兩個運算元執行按位與運算。
  • bit_or − 對兩個運算元執行按位或運算。
  • Bit_xor − 對兩個運算元執行按位異或 (XOR) 運算,並返回相應的輸出。

實用工具

實用工具是指一系列通用元件,它們在程式設計中提供基本功能,並允許對資料和型別進行高效操作。

1. 對和元組

  • pair − 它是一個容器,包含兩個可能不同型別的值,作為第一個和第二個成員。
  • tuple − 一個固定大小的集合,可以儲存多種不同型別的多個值,可以透過 std::get 訪問每個元素。

2. 智慧指標

  • unique_ptr − 它是一個智慧指標,擁有一個動態分配的物件,當指標超出範圍時會自動釋放它。
  • Shared_ptr − 它是一個智慧指標,允許多個指標共享一個動態分配物件的擁有權,透過使用引用計數來管理生命週期。
  • Weak_ptr − 它是一個智慧指標,提供對由 std::shared_ptr 管理的物件的非擁有引用,這可以防止迴圈引用。

3. 可選、變體和任何型別 (C++17)

  • Optional − 它是一個包裝器,表示一個可選的值,指示值是否存在。
  • variant − 它是一個型別安全的聯合,可以儲存多個指定型別之一,提供了一種在不產生歧義的情況下處理多種型別的方法。
  • any − 它是一個型別安全的容器,用於儲存任何型別的值,允許動態型別擦除和執行時型別檢查。

4. 記憶體管理和數值限制

  • allocator − 用於管理動態記憶體。
  • numeric_limits − 提供有關基本資料型別屬性的資訊。

5. 型別特徵

它是一組模板,允許您執行編譯時型別檢查。

  • is_same − 它是一個型別特徵,檢查兩個型別是否相同,如果相同則返回真,否則返回假。
  • is_integral − 它是一個型別特徵,確定給定型別是否為整型(例如,int、char、long),對於整型返回真,對於其他型別返回假。
  • is_floating_point − 它是一個型別特徵,檢查給定型別是否為浮點型別(例如,float、double、long double),對於浮點型別返回真,對於非浮點型別返回假。
  • enable_if − 它是一個實用工具,根據編譯時條件啟用或停用模板例項化,通常用於 SFINAE(替換失敗不是錯誤)。

基本 STL 功能:簡單示例

這是一個演示一些基本 STL 功能的簡單示例

#include <iostream>
#include <vector>
#include <algorithm>

int main() {
   std::vector<int> vec = {5, 2, 8, 1, 3};

   // Sorting the vector
   std::sort(vec.begin(), vec.end());

   // Displaying sorted elements
   std::cout << "Sorted vector: ";
   for (int num : vec) {
      std::cout << num << " ";
   }
   std::cout << std::endl;

   // Finding an element
   auto it = std::find(vec.begin(), vec.end(), 3);
   if (it != vec.end()) {
      std::cout << "Element 3 found at position: " 
         << std::distance(vec.begin(), it) << std::endl;
   } else {
      std::cout << "Element 3 not found" << std::endl;
   }

   return 0;
}

輸出

Sorted vector: 1 2 3 5 8 
Element 3 found at position: 2

解釋

此程式碼在多個地方提供了 STL 的使用,

  • std::vector<int> vec − 上述程式使用了 std::vector,它是一個 STL 容器,允許動態陣列管理。
  • std::sort(vec.begin(), vec.end()) − 此函式是 STL 演算法的一部分,它作用於容器,按升序對向量的元素進行排序。
  • std::find(vec.begin(), vec.end(), 3) − 此函式在向量中搜索值 3,如果找到則返回指向它的迭代器。
  • begin() 和 vec.end() − 這些函式分別返回指向向量開頭和向量末尾之後的迭代器。
  • std::distance(vec.begin(), it) − 此函式計算兩個迭代器之間的元素數量(在本例中,從向量的開頭到找到的迭代器),有效地給出找到的元素的索引。

使用 STL 的好處

在 C++ 中使用標準模板庫 (STL) 有很多好處,因為它提供了一個龐大的預定義資料結構和演算法集合,從而節省了時間和精力,減少了從頭實現這些結構和演算法的需要。它還促進了程式碼重用、模組化、型別安全和靈活性。這使得相同的程式碼可以與不同的資料型別一起工作。總的來說,它提高了程式設計效率和程式碼質量。

廣告