C++標準庫概述


在使用 C++ 編程中,最重要的庫就是 C++ 標準庫。C++ 標準庫的核心是泛型容器和泛型算法,庫中的這一子集通常稱爲標準模板庫(Standard Template Library, STL),因爲這一部分大量使用了模板。STL 的威力在於提供了泛型容器和泛型算法,使大部分算法可用於大部分容器,而無論容器中保存的數據類型是什麼。性能是 STL 中非常重要的一部分。STL 的目標是要讓 STL 容器和算法和手工編寫的代碼速度相當快。

編碼原則

標準庫大量使用了C++的模板特性和運算符重載特性,標準模板庫也是這樣。

C++標準庫概述

字符串

從技術角度,C++ string實際上是basic_string模板char實例化的typedef名稱。然而,不需要關注這些細節。只要像非模板類那樣使用string即可。

正則表達式

I/O流

I/O功能在幾個頭文件中定義:

<fstream>
<iomanip>
<ios>
<iosfwd>
<iostream>
<istream>
<ostream>
<sstream>
<streambuf>
<strstream>

智能指針

C++用智能指針unique_ptrshared_ptrweek_ptr解決內存管理問題。

shared_ptrweek_ptr是線程安全的,都在<memory>中定義。

異常

C++標準庫提供了一個異常的類層次結構,在程序中可以使用這些類,也可以通過繼承方式創建自己的異常類型。

異常在以下頭文件中定義:

<exception>
<stdexcept>
<system_error>

數學工具

雖然這些類模板化了,可以用於任何類型,但是一般不認爲這些類是標準模板庫的一部分。

  • <complex>提供了一個複數類complex,提供了對實部和虛部的數的操作抽象。

  • <ratio>有理數運算庫,可以精確地表示任何由分子和分母定義的有限有理數。

  • <valarray>提供了valarray,和vector類似,但是對高性能數值應用做了特別的優化,提供了表示矢量切片概念的相關類。通過這些構件,可以構建執行矩陣數學運算的類。不過,沒有內建的矩陣類。Boost這樣的第三方庫提供了矩陣支持。

獲取數值極限的標準方式。

<limts>中的numeric_limits模板。

cout << "Max int value: " << numeric_limits<int>::max() << endl;
cout << "Min int value: " << numeric_limits<int>::min() << endl;
cout << "Lowest int value: " << numeric_limits<int>::lowest() << endl;
cout << "Max double value: " << numeric_limits<double>::max() << endl;
cout << "Min double value: " << numeric_limits<double>::min() << endl;
cout << "Lowest double value: " << numeric_limits<double>::lowest() << endl;

時間工具

<chrono>簡化了時間相關的操作,特定時間間隔的定時操作和定時相關的操作執行。

隨機數

srand()rand()只提供了非常初級的隨機數,也無法修改生成隨機數的分佈。

C++11添加了一個完善的隨機數庫<random>,帶有隨機數引擎、隨機數引擎適配器以及隨機數分佈。

通過<random>可以生成特定問題域的隨機數,例如正態分佈、負指數分佈。

初始化列表

<initializer_list>編寫參數數目可變的函數。

Pair和Tuple

  • <utility>定義了pair模板,可以用於存儲兩種不同類型的元素。這稱爲存儲異構元素。

  • <tuple>定義的tuplepair的一種泛化,是一個固定大小的序列,元組的元素可以是異構的。

函數對象

實現函數調用運算符的類稱爲函數對象。函數對象可以用作某些STL算法的謂詞。

<functional>

多線程

單個線程可以用<thread>建立。

多線程代碼,需要考慮:幾個線程不能同時讀寫同一個數據。爲了避免這種情形,可以使用<atomic>定義的原子性。

<condition_vaiable><mutex>提供了其他線程同步機制。

如果只需計算某個數據,得到結果,並具有相應的異常處理,使用<future>頭文件定義的asyncfuture要比thread簡單。

類型特質

類型特質在<type_traits>中定義,提供了編譯期間的類型信息。編寫高級模板的時候可以使用它。

標準模板庫

C++ STL(Standard Template Library, STL)容器是同構的,每種容器只允許一種類型的元素。

注意:C++標準定義了每個容器和算法的接口(interface),沒有定義實現。因此,不同的供應商可以自由提供不同的實現。不過,作爲接口的一部分,標準定義了性能需求。

STL容器

vector

如果在程序中需要快速訪問元素,但是不會頻繁在中間添加或刪除元素,則應該使用vector

在任何可能的情況下使用vector而不是C風格的數組

vector<bool>有特定優化過的模板,不過標準沒有規定如何實現優化空間。

list

雙向鏈表

forward_list(C++11)

只支持單向遍歷的單鏈表。內存需求比list小。

deque

雙端隊列(double-ended queue)。不過行爲更像vector,而不是queue

提供了快速的元素訪問(常量時間),在序列兩端還提供了快速的插入和刪除(攤還常量時間),但是在序列中間插入和刪除的速度較慢(線性時間)。

如果需要在兩頭快速插入或刪除元素,還要求快速訪問所有元素,那麼應該使用deque而不是vector

大部分場景並不滿足這種需求,因此大部分情況下,vectorlist足以滿足需求。

array

標準C風格數組的替代品。特別適用於大小固定的集合,而且沒有vector的開銷。

使用array比C風格數組有幾點好處:

  • 總能知道自己的大小;

  • 不會自動轉換爲指針類型,避免某些bug;

  • 大小固定,允許堆棧上分配內存,不需要像vector一樣需要堆訪問權限。

vectorlistdequearrayforward_list容器都稱爲順序容器(sequential container),因爲它們保存的是元素的序列。

queue

先進先出的隊列

priority_queue

優先級隊列。插入和刪除比簡單的queue插入和刪除慢。

stack

先入後出

從技術角度,queuepriority_queuestack容器都是容器適配器(adapter)。它們只是構建在某種標準順序容器(vector、list或deque)上的簡單接口。

set和multiset

類似數學上的集合。不過set中按照一定的順序保存。排序的原因是當客戶枚舉元素時,能夠以operator<或用戶自定義的比較器的順序出現。

set提供了對數時間的插入、刪除和查找。如果要存儲重複元素,就必須使用 <set> 頭文件定義的 multiset

map和multimap

map 保存的是鍵/值對。map 按照排好的順序保存元素,排序的依據是鍵值而非對象值。在其他所有方面,mapset 是一致的。如果需要關聯鍵和值,就應該使用 map

multimap 也在 <map> 中定義,它和 map 的關係等同於 multisetset 的關係。確切地講,multimap 是一個允許重複鍵的 map

setmap容器都是關聯容器,因爲它們關聯了鍵和值。在set中,鍵本身就是值。這些容器會對元素進行排序,因此這些容器稱爲排序或者有序關聯容器。

無序關聯容器/哈希表

哈希表也稱爲無序(unordered associative container)。有4個無序關聯容器:

  • unordered_map
  • unordered_set
  • unordered_multimap
  • unordered_multiset

在C++11之前,哈希表不屬於C++標準庫的一部分,因此很多第三方庫都用hash作爲前綴。因此C++標準委員會決定使用unordered而不是hash,避免名稱衝突。

bitset

bitset不是常規意義的容器,不能插入和刪除元素。固定大小,不支持迭代器。可以想象成一個可以讀寫的布爾值序列。

bitset不侷限於int或者其他原始類型的大小。可以在聲明時指定:bitset<N>

vector應該是默認使用的容器。實際上,vector中的插入和刪除常常快於list或者forward_list。這是現代CPU上內存和緩存的工作方式,而listforward_list需要先移動到要插入或刪除元素的位置上。list或者forward_list的內存可能是碎片化的,所以迭代慢於vector

STL算法

STL採取了分離數據(容器)和功能(算法)的方式。看上去有點違背了面向對象編程的思想,但是爲了在STL中支持泛型編程,有必要這麼做。

正交性的指導原則使算法和容器分離開,(幾乎)所有算法都可以用於(幾乎)所有容器。

有些容器以類方法提供了某些算法,因爲泛型算法在特定容器上表現不出色。**比如,set提供了自己的find()算法,比泛型的find()算法快。**如果類提供了特定的算法實現,應該使用容器特定方法的形式。

泛型算法並不是直接對容器操作。泛型算法使用迭代器(iterator)作爲中介。

函數名 概要
begin(), end() 第一個元素和最後一個元素後面的元素,返回非const迭代器
以下是C++14新增的迭代器
cbegin(), cend() 第一個元素和最後一個元素後面的元素,返回const迭代器
rbegin(), rend() 非const的反向迭代器
crbegin(), crend() const的反向迭代器

非修改順序算法

有了這些算法後,幾乎不再需要編寫for循環來迭代值序列了。【就是暴力枚舉的封裝形式】

算法名稱 算法概要 複雜度
adjacent_find() 查找第一個兩個連續元素相等或匹配謂詞的實例 線性
find()、find_if() 查找第一個匹配值或使謂詞返回true的元素 線性
find_first_of() find類似,只是同時搜索多個元素中的一個 二次
find_if_not() 查找第一個使謂詞返回false的元素 線性
search()、find_end() 在序列中查找第一個(search())或最後一個(find_end())匹配另一個序列的子序列,或者這個子序列的元素和謂詞指定的一致 線性

搜索算法
不需要元素有序

比較算法

不要求有序,最差複雜度爲線性複雜度

算法名稱 算法概要
equal() 檢查相應元素是否相等或匹配謂詞,以此判斷兩個序列是否相等
mismatch() 返回每個序列第一個出現的和其他序列同一位置元素不匹配的元素
lexicographical_compare() 比較兩個序列,判斷這兩個序列的“詞典順序”。將第一個序列中的每一個元素和第二個序列中對應的元素進行比較。如果一個元素小於另一個元素,那麼這個序列按照詞典順序在前面。如果兩個元素相等,則按順序比較下一個元素

工具算法

算法名稱 算法概要
all_of() 序列爲空或對所有謂詞返回true,則返回true,否則返回false
any_of() 序列爲空或至少謂詞判斷有一次true,則返回true,否則返回false
none_of() 序列爲空或者謂詞對所有元素返回false,則返回true,否則返回false
count()、count_if() 計算匹配一個值或者使謂詞返回true的元素個數

修改序列算法

算法名稱 算法概要
copy()、copy_backward() 將一個序列的元素複製到另一個序列
copy_if() 將謂詞返回true的元素複製到另一個序列
copy_n() 複製n個元素到另一個序列
fill() 所有元素設置爲一個新值
fill_n() 前n個元素設置爲一個新值
generate() 調用指定函數,爲序列中每一個元素生成新值
generate_n() 調用指定函數,爲序列中前n個元素生成一個新值
move(),move_backward() 將一個序列的元素移到另一個序列。使用了高效的移動語義
remove()、remove_if()、remove_copy()、remove_copy_if() 刪除匹配給定值或使謂詞返回true的元素,就地刪除或將結果複製到另一個不同的序列
replace()、replace_if()、replace_copy()、replace_copy_if() 將匹配特定值或導致謂詞返回true的所有元素替換爲新元素,就地替換或將結果複製到新序列
reverse()、reverse_copy() 掉轉序列中的元素,原地操作或者複製到新序列
rotate()、rotate_copy() 交換序列中前半部分和後半部分,原地操作或者複製到新序列。兩個要交換的子序列不一定要一樣大
shuffle()、random_shuffle() 打亂元素的順序,random_shuffle()在C++14之後被廢棄
transform() 對序列中的每個元素調用一元函數,或對兩個隊列中的對應元素調用二元函數
unique()、unique_copy() 在序列中刪除連續出現的重複元素,原地刪除或複製到新序列

操作算法
for_each:對每個元素執行函數

交換算法

算法名稱 算法概要
iter_swap()swap_ranges() 交換兩個元素,或交換兩個元素的序列
swap() 交換兩個值,在<utility>頭文件中定義

分區算法

排序算法

算法名稱 算法概要 複雜度
is_sorted()、is_sorted_until() 檢查一個序列是否排序,或者哪個子序列已經排序 線性
nth_element() 重定位序列中的第n個元素,使第n個位置的元素就是排好序之後第n個位置的元素。該函數會重新安排所有元素,使第n個元素前面的所有元素都小於新的第n個元素 線性
partial_sort()、partial_sort_copy() 只排序序列中的一部分元素:只有前n個元素(由迭代器指定)排序,其餘元素不排序。在原位置排序,或者複製到新的序列 線性對數
sort()、stable_sort() 在原位置排序,保留重複元素的順序或不保留 線性對數

二叉搜索樹算法

算法名稱 算法概要 複雜度
lower_bound()、upper_bound()、equal_range() 查找包含給定元素的範圍的頭(lower_bound())、尾(upper_bound())或兩端(equal_range()) 對數
binary_search() 在序列中查找一個值 對數

集合算法

這些算法最適合set,但是也能操作大部分容器排序後的序列。【歸併排序】

算法名稱 算法概要 複雜度
inplace_merge() 在原位置將兩個排好序的序列合併 線性對數
merge() 合併兩個排好序的序列,將兩個序列複製到新的序列 線性
includes() 確定是否序列中的每一個元素都在另一個序列中 線性
set_union()、set_intersection()、set_difference()、set_symmetric_difference()[並、交、差、補] 在兩個排序的序列上執行特定的集合操作,將結果複製到第三個排序的序列中 線性

堆算法

堆(heap)是一個標準的數據結構,數組或序列中的元素在其中以半排序的方式排序,因此能夠快速找到“頂部”的元素。通過使用6個算法可以對序列進行堆排序。

算法名稱 算法概要 複雜度
is_heap() 檢查某個範圍的元素是否是一個堆 線性
is_heap_until() 在給定範圍內的元素中查找最大的子範圍 線性
make_heap() 從一個範圍的元素中創建堆 線性
push_heap()pop_heap() 在堆中添加或刪除元素 對數
sort_heap() 把堆轉換爲升序排列的元素範圍 線性對數

最大/最小算法

算法名稱 算法概要
min()max() 返回兩個值中最小值或最大值
minmax() pair 方式返回兩個或多個值中的最小值和最大值
min_element()max_element() 返回序列中最小或最大元素
minmax_element 找到序列中最小和最大元素,把結果返回爲 pair

數值處理算法
<numeric>,所有的算法都是線性複雜度,不要求排序源序列。

算法名稱 算法概要
accumulate() “累加”一個序列中所有的元素的值,默認行爲是計算元素的和,但調用者可以提供不同的二元函數
adjacent_difference() 生成一個新的序列,其中每一個元素都是對應元素和源序列中之前元素的差(或其它二元操作)
inner_product() accumulate() 類似,但對兩個序列操作。對序列中的並行元素調用二元函數(默認做乘法),通過另一個二元函數(默認加法)累加結果值。如果序列表示數學矢量,那麼這個算法計算矢量的點積

置換算法

算法名稱 算法概要 複雜度
is_permutation() 如果一個範圍中的元素是另一個範圍中的元素的轉換,就返回true 二次
next_permutation()、prev_permutation() 修改序列,將序列轉換爲下一個或前一個排列。如果從正確排序的序列開始,連續調用可以獲得所有可能的排列。如果沒有更多排列,則返回false 線性

STL中還缺什麼

  • STL不能保證任何線程安全

  • STL沒有提供任何泛型的樹結構或圖結構(不過大部分語言都沒有提供),如果編寫解析器,就需要自己實現或者尋找其他庫。

最後

STL 是可擴展的,可以編寫適用於現有算法和容器的容器和算法。如果 STL 沒有提供需要的內容,可以考慮編寫兼容 STL 的代碼。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章