STL歷史簡介

2021-06-20 20:22:09 字數 4520 閱讀 8285

標準模板庫,也叫 stl,是乙個 c++ 容器類庫,演算法和迭代器。它提供許多基本演算法,資料結構。stl 是乙個通用庫,即可以充分定製:幾乎所有的 stl 元件都是模板。在你使用 stl 前,你必須了解模板的工作情況。

容器和演算法

和許多類庫一樣,stl 包含容器類 - 可以包含其他物件的類。stl 包含向量類,鍊錶類,雙向佇列類,集合類,圖類,等等。他們中的每個類都是模板,能包含各種型別的物件。例如,你可以用 vector,就象常規的 c 語言中的陣列,除了 vector 不要你象陣列那樣考慮到動態記憶體分配的問題。

vectorv(3); // 定義乙個有三個元素的向量類

v[0] = 7;

v[1] = v[0] + 3;

v[2] = v[0] + v[1]; // v[0] == 7, v[1] == 10, v[2] == 17

stl 還包含了大量的演算法。他們巧妙地處理儲存在容器中的資料。你能夠顛倒 vector 中的元素,只是簡單使用 reverse 演算法。

reverse(v.begin(), v.end()); // v[0] == 17, v[1] == 10, v[2] == 7

在呼叫 reverse 的時候有兩點要注意。首先,他是個全域性函式,而不是成員函式。其次,他有兩個引數,而不是乙個:他操作一定範圍的元素而不是操作容器。 在這個例子中他正好是對整個容器 v 操作。

以上兩點的原因是相同的:reverse 和其他 stl 演算法一樣,他們是通用的,也就是說, reverse 不僅可以用來顛倒向量的元素,也可以顛倒鍊錶中元素的順序。甚至可以對陣列操作。下面的程式是合法的。

double a[6] = ;

reverse(a, a + 6);

for (int i = 0; i < 6; ++i)

cout << "a[" << i << "] = " << a[i];

這個例子也用到了範圍,和我們上面的向量的例子一樣:第乙個引數是指向要操作的範圍的頭的指標,第二個引數是指向尾的指標。在這裡,範圍是 [a, a + 6)。可能你注意到範圍的不對稱。

迭代器在上面的 c 陣列的例子中,reverse 的引數型別是 double*。那麼在 向量 或鍊錶中引數又是什麼呢?也就是說,究竟 reverse 是如何定義引數的,並且 v.begin() 和 v.end() 返回什麼?

問題的答案是 reverse 的引數是 迭代器 (iterators)。他是指標的概括。指標自己也是迭代器。這也是為什麼可以顛倒陣列中元素的順序的原因。同樣地,向量 定義了巢狀的型別 iterator 和 const_iterator。在上面的例子中, v.begin() 和 v.end() 返回的型別是 vector::iterator。還有一些迭代器,如 istream_iterator 和 ostream_iterator。他們根本不能和容器關聯。

迭代器的出現讓演算法和容器的分離成為可能。演算法是模板,其型別依賴於迭代器,因此不會侷限於單一的容器。例如,我們寫個演算法實現在一定範圍的線性查詢。下面就是 stl 的 find 演算法。

template

inputiterator find(inputiterator first, inputiterator last, const t& value)

find 有三個引數:兩個迭代器定義範圍,第三個是要查詢的值。他遍歷範圍 [first, last),從頭到尾的處理,在找到後停止或到範圍的最後停止。

first 和 last 定義為 inputiterator 型別,而 inputiterator 是個模板引數。也就是說,實際上沒有乙個真實的型別 inputiterator:當你呼叫 find 時,編譯器用實際的型別去匹配 inputiterator 和 t。如果 find 的兩個引數中第乙個實現是 int*,第三個實現是 int,那麼你可以認為是在執行下面的函式。

int* find(int* first, int* last, const int& value)

約束和類屬實參

對於函式模板 (不僅僅是 stl 演算法) 來說,乙個非常重要的問題是:用什麼樣的引數型別去匹配模板引數才正確?為詳細說明這個問題,我們舉例:顯然,int* 或 double* 能夠代替 find 的模板引數 inputiterator。而 int 或 double 則不行。因此,有個顯然的答案是:find 隱含地定義了對於型別的要求。無論用什麼型別來實現 inputiterator,都必須提供一定的操作:他必須能夠比較兩個物件的大小,他必須能夠進行增加操作。

find 不是僅有的乙個有如此要求的 stl 演算法。for_each 和 count 以及其他演算法,都要滿足上面的要求。這些要求很重要,所以我們給他取個名字:我們將這一類型別條件叫約束 (concept)。我們稱上面的約束為 input iterator。如果乙個型別滿足所有的要求,那麼我們說這個型別符合該約束,或者說他是該約束的類屬實參 (model)。我們說 int* 是 input iterator 的類屬實參,因為 int* 能提供 input iterator 所要求的所有操作。

約束可不是 c++ 語言的一部份,在乙個程式中你沒有辦法去定義乙個約束,或者定義某型別是約束的類屬實參。然而,約束是 stl 中的很重要的一部份。使用約束類屬機制讓我們在程式中從程式實現中分離介面成為可能。find 的作者只要考慮介面只針對於 input iterator 而不是去實現符合該約束的所有可能資料型別。同樣,如果你要用 find,那麼你只要確定你傳遞的引數是 input iterator 的類屬實參就可以了。這就是為什麼 find 和 reverse 能夠在 list, vector, c 數組合其他型別中使用。用約束(的思想)去程式設計,而不是固定於某一特定型別,讓程式重用元件和將元件組合使用。

refinement

input iterator 實際上是相當弱的約束。也就是說,他只有少數幾個要求。乙個 input iterator 必須支援指標運算的子集(他必須能夠通過操作符 operator++ 來增加 input iterator ),但是不需要其支援所有的指標演算法。這對於 find 來說足夠了,但是其他的一些演算法就不是這樣的,他們需要滿足另外的條件。例如 reverse 還要能夠支援操作符 --。用術語來說,就是 reverse 的引數要是 bidirectional iterator 而非 input iterator。

bidirectional iterator 的約束和 input iterator 的約束很相近:他只是簡單地增加了一點另外的條件。bidirectional iterator 的類屬實參是 input terator 類屬實參的子集:每個是 bidirectional iterator 類屬實參的資料型別都是 input iterator 的類屬實參。例如 int* 即是 bidirectional iterator 的類屬實參,也是 input iterator 的類屬實參。但是 istream_iterator就不同了,他只是 input iterator 的類屬實參:他不能「適應」更嚴格的 bidirectional iterator 的要求。

我們這樣來描述 input iterator 和 bidirectional iterator 的關係: bidirectional iterator 是 input iterator 的 refinement。約束的 refinement 非常象 c++ 類的繼承。我們用不同的詞的主要原因是 refinement 用於約束而不是實際的型別。

除了我們上面提到的兩個迭代器約束外,還有三個迭代器約束。這五個迭代器 約束是: output iterator, input iterator, forward iterator, bidirectional iterator 和 random access iterator。forward iterator 是 input iterator 的 refinement,bidirectional iterator 又是 forward iterator 的 refinement, random access iterator 是 bidirectional iterator 的 refinement。(output iterator 和其他四個約束有關係,但不是 refinement 層次上的關係:他不是其他四個的 refinement,其他四個也不是他的 refinement。)

容器類和迭代器一樣,也有約束的層次關係。所有的容器是 container 的類屬實參。例如 sequence 和 associative container,都是具體的容器。

stl 的其他部份

如果你理解了演算法,迭代器,容器,那麼你幾乎就了解了 stl。但是 stl 還包含其他幾個部份的內容。

首先,stl 包含幾個 「工具」:非常基本的在類庫中不同地方出現的函式和 concept。例如,assignable 約束, 就描述了那些可以賦值和類的拷貝操作的型別。幾乎所有的 stl 類都是 assignable 的類屬實參。幾乎所有的演算法都要求其引數是 assignable 的類屬實參。

其次,stl 包括了底層的記憶體的分配和釋放。allocators 非常少見,你幾乎可以忽略他們。

最後,stl 包含了大量的 物件函式,也稱為 functors。就象迭代器是指標的一般形式一樣,物件函式是函式的一般形式。物件函式有幾種不同的約束,包括 unary function (一種只有乙個引數的物件函式,如 f(x)) 和 binary function (一種有兩個引數的物件函式,如 f(x, y))。物件函式對於程式設計來說很重要,因為他如同物件型別的抽象一樣作用於操作。

STL容器簡介

stl的容器可以分為以下幾個大類 一 序列容器,有vector,list,deque,string.二 關聯容器,有set,multiset,map,mulmap,hash set,hash map,hash multiset,hash multimap 三 其他的雜項 stack,queue,va...

STL容器簡介

1 stl簡介 2 stl中的容器及底層實現 3 stl容器的公用函式 stl提供六大元件,彼此可以組合套用 1 容器 容器用來存放資料,從實現的角度看,stl容器是一種類模板,包括vector list deque set map等。2 演算法 stl演算法是一種函式模板,各種常用的演算法如sor...

雷達發展歷史簡介

成像通常分為主動成像和被動成像。主動成像主動發射訊號然後成像 被動成像則不需主動發射訊號即可成像。通常,普通的照相機 不使用閃光燈 雷射等輔助手段 是被動成像裝置 雷達則通常是主動成像裝置 個別雷達除外 通過發射電磁波並接收反射回來的訊號進行成像。本文主要對雷達的波段和發展史做簡單介紹。由於光速受介...