1.STL簡介
1.1標準模板庫STL(Standard Template Library)
1.2C++ 基礎
1.2.1類.
1.2.2函數對象(Function Objects).
1.2.3模板(Template)
2.容器(Container)
2.1向量(Vector)
2.2線性表(List)
2.3雙向隊列(Deque)
2.4關聯容器(Container)
3.迭代器(Iterator)
3.1 輸入和輸出迭代器
3.2向前迭代器
3.3雙向迭代器
3.4任意存取迭代器
3.5迭代標籤(Iterator Tag)
4. 算法和函數對象
4.1如何創建泛型算法
4.2STL 算法
5. 適配器(Adaptor)
5.1 容器適配器
5.2迭代適配器
5.3函數適配器
6 其餘的STL部件
1.STL簡介
1.1標準模板庫STL(StandardTemplate Library)
(1)STL部件庫
STL是個部件庫(component library) ,其中的部件包括有容器(container ,儲存任意類型對象的對象)和算法。只要用戶對自己定義的部件做點小小的要求,STL 中的算法就可以工作在用戶自定義的容器上,或者STL 中的容器可以使用用戶自定義的算法。
我們可以把軟件部件想象成一個三位空間:
第一維表示數據類型(int, double, char, …);
第二維表示容器(array, linked-list, …);
第三維表示算法(sort, merge, search, …) 。
STL具體化了上述思想,期望通過減少開發時間以簡化軟件開發,簡化調試、維護並增加代碼的可移植性。
STL包含5 個主要的部分:
·算法(Algorithm) :能運行在不同容器(container) 上的計算過程。
·容器(Container) :能夠保留並管理對象的對象。
·迭代器(Iterator):算法存取容器(algorithm-access to containers) 的抽象,以便算法可以應用在不同的容器上。
·函數對象(Function Object) :定義了函數調用操作符(operator()) 的類。
·適應器(Adaptor) :封裝一個部件以提供另外的接口(例如用list實現stack)。
(2)STL使用
STL使用時很簡單:
//MyFile.cpp
#include“stdafx.h”
…
#include<vector> //include 你想用的STL頭文件
usingnamespace std; // 一定要寫上這句話,因爲STL 都是在std名字空間中定義
void F1()
{
…
vector<int> v(10); // 現在你就可以放心大膽的使用STL 了
…
}
1.2 C++ 基礎
1.2.1 類.
請看下面一段代碼:
class shape
{
private:
intx_pos;
inty_pos;
intcolor;
public:
shape(): x_pos(0), y_pos(0), color(1) {}
shape(intx, int y, int c = 1) : x_pos(x), y_pos(y), color(c) {}
shape(constshape& s) : x_pos(s.x_pos), y_pos(s.y_pos), color(s.color) {}
~shape(){}
shape&operator=(const shape& s)
{
x_pos= s.x_pos;
y_pos= s.y_pos;
color= s.color;
return*this;
}
intget_x_pos() { return x_pos; }
intget_y_pos() { return y_pos; }
intget_color() { return color; }
voidset_x_pos(int x) { x_pos = x; }
voidset_y_pos(int y) { y_pos = y; }
voidset_color(int c) { color = c; }
virtualvoid DrawShape() {}
friendostream& operator<<(ostream& os, const shape& s);
};
ostream&operator<<(ostream& os, const shape& s)
{
os<< “shape: (“ << s.x_pos << “,” << s.y_pos << “,”<< s.color << “)”;
returnos;
}
1.2.2 函數對象(FunctionObjects).
所謂函數對象(function object) 是定義了函數調用操作符(funiton-call operator ,即
operator())的對象。
請看下面的例子:
class less
{
public:
less(intv) : val(v) {}
intoperator()(int v)
{
returnv < val;
}
private:
int val;
};
含義:
聲明一個less 對象:less less_than_five(5);
當調用function-call operator 時,看判斷出傳入的參數是否小於val:
cout<< “2 is less than 5: “ << (less_than_five(2) ? “yes” : “no”);
輸出是:2is less than 5: yes
函數對象在使用STL 時非常重要,你需要熟悉這樣的編碼形式。在使用STL 時,經常需要把函數對象作爲算法的輸入參數,或着實例化一個容器(container)時的輸入參數。
1.2.3 模板(Template)
(1)函數模板
請看下面的代碼:
void swap(int& a, int& b)
{
inttmp = a;
a= b;
b = tmp;
}
這個函數swap 很簡單:把兩個整數的值交換一下。但當你寫一個大程序時,經常需要交換float、long 、char等變量,甚至如上面定義的shape 變量,這時候你就需要把上面的代碼修改一下,適應各自的參數類型,然後拷貝到很多處各自需要的地方。一旦需要對這塊代碼做些改動的時候,就必須把所有用到這塊代碼的地方一一做相應的修改,解決這個問題的方法就是用模板(template):
template <class T>
void swap(T& a, T& b)
{
Ttmp = a;
a= b;
b= tmp
}
注意這裏T是任意的類型名字。看例子:
int a = 3, b =5;
shape MyShape, YourShape;
float fa = 3.01, fb = 5.02;
swap(a , b); // 交換整數
swap(MyShape, YourShape); // 交換自定義類的兩個對象的值
swap(fa, fb); // 交換浮點數
還可以看看兩個函數模板例子:
template <class T>
T& min(T& a, T& b) // 取兩個元素中的最小者
{
returna < b ? a : b;
}
template <class T>
void pritn_to_cout(char* msg, T& obj) // 把一個元素輸出到cout 上
{
cout<< msg << “: “ << obj << endl;
}
使用後者時,要求類型T 中定義了put-to 操作符(operator <<)。
(2)類模板
創建類模板的動機和容器類的使用是密切相關的。比如一個容器類棧(stack),定義者並
不關心棧中對象的類型,而使用stack 者自己指定到底包含什麼對象類型。
下面看一個向量(vector) 例子:
template <class T>
class vector
{
T*v;
intsz;
public:
vector(ints) { v = new T[sz = s]; }
~vector(){ delete [] v; }
T&operator[] (int i) { return v[i]; }
intget_size() { return sz; }
};
現在可以實例化不同類型的vector 容器了:
vector<int> int_vector(10);
vector<char> char_vector(10);
vector<shape> shape_vector(10);
(3)模板特化
也許在某些情況下,編譯器對某種類型產生的模板代碼不另你滿意,你也可以爲這種類
型給出一個特定的實現,此之謂“模板特化(template specialization)”。比如,你想讓shape
類型的vector只包含一個對象,則可以特化vector 模板如下:
class vector<shape>
{
shape v;
public:
vector(shape&s) : v(s) {}
shape&operator[] (int i) { return v; }
intget_size() { return 1; }
};
使用時:
shape MyShape;
vector<shape>single_shape_vector(MyShape);
STL 中有時會需要定義特定類型的算法或容器實現。
2.容器(Container)
容器(Container)是能夠保存其他類型的對象的類。容器形成了STL 的關鍵部件。當書寫任何類型軟件的時候,把特定類型的元素集聚起來都是很至關重要的任務。
STL中支持的容器如下:
順序容器(Sequence Container):向量(Vector)、雙向隊列(Deque)、線性表(List)。
關聯容器(Associative Container):集合(Set)、多重集合(MultiSet)、映射(Map)、多重映射(MultiSet)。
順序容器組織成對象的有限線性集合,所有對象都是同一類型。STL 中三種基本順序容器是:向量(Vector) 、線性表(List)、雙向隊列(Deque) 。
關聯容器提供了基於KEY 的數據的快速檢索能力。元素被排好序,檢索數據時可以二
分搜索。STL有四種關聯容器。當一個KEY 對應一個Value時,可以使用集合(Set)和映射
(Map);若對應同一KEY 有多個元素被存儲時,可以使用多集合(MultiSet)和多映射
(MultiMap) 。
2.1 向量(Vector)
如果你要將所有shape 對象保存在一個容器中,用C++代碼可以這樣寫:
shape my_shapes[max_size];
這裏max_size 是可以保存在my_shapes 數組中的最大數量。
當你使用STL 時,則可以這樣寫:
#include <vector>
using namespace std;
int main()
{
vector<shape> my_shapes; // 使用my_shapes
…
return0;
}
想得到容器中能保存的最大元素數量就可以用vector 類的成員函數max_size():
vector<shape>::size_type max_size =my_shapes.max_size();
當前容器的實際尺寸 ---已有的元素個數用size():
vector<shape>::size_type size =my_shapes.size();
就像size_type 描述了vector 尺寸的類型,value_type 說明了其中保存的對象的類型:
cout << “value type: “ <<typeid(vector<float>::value_type).name();
輸出:valuetype: float
可以用capacity() 來取得vector 中已分配內存的元素個數:
vector<int> v;
vector<int>::size_type capacity =v.capacity();
vector類似於數組,可以使用下標[]訪問:
vector<int> v(10);
v[0] = 101;
注意到這裏預先給10 個元素分配了空間。你也可以使用vector 提供的插入函數來動態的擴展容器。成員函數push_back() 就在vector 的尾部添加了一個元素:
v.push_back(3);
也可以用insert()函數完成同樣的工作:
v.insert(v.end(), 3);
這裏insert()成員函數需要兩個參數:一個指向容器中指定位置的迭代器(iterator),一個待插入的元素。insert()將元素插入到迭代器指定元素之前。
現在對迭代器(Iterator)做點解釋。Iterator 是指針(pointer)的泛化,iterator 要求定義
operator*,它返回指定類型的值。Iterator 常常和容器聯繫在一起。例子:
vector<int> v(3);
v[0] = 5;
v[1] = 2;
v[2] = 7;
vector<int>::iterator first =v.begin();
vector<int>::iterator last = v.end();
while (first != last)
cout << *first++ << “ “;
上面代碼的輸出是:5 2 7
begin()返回的是vector 中第一個元素的iterator ,而end()返回的並不是最後一個元素的iterator ,而是pastthe last element 。在STL 中叫past-the-enditerator 。
begin() 返回的iteratorend()返回的iterator
區間[being(), end() ):
兩個迭代器給定的區間在STL 中非常重要,因爲STL 算法中大量的使用區間。比如排序算法:
sort(begin_iterator, past_the_end_iterator);
第一個參數指向區間的第一個元素,而第二個參數指向區間的path-the-end 元素。
有了迭代器作爲媒介,我們就可以把算法(algorithm) 和容器(container) 分開實現了:
可以用以下的幾種方法聲明一個vector 對象:
vector<float> v(5, 3.25); // 初始化有5 個元素,其值都是3.25
vector<float> v_new1(v);
vector<float> v_new2 = v;
vector<float> v_new3(v.begin(),v.end());
這四個vector對象是相等的,可以用operator== 來判斷。
其餘常用的vector成員函數有:
empty() :判斷vector是否爲空
front():取得vector的第一個元素
back() :取得vector的最後一個元素
pop_back() :去掉最後一個元素
erase():去掉某個iterator或者iterator 區間指定的元素
到現在我們已經可以把一些對象存儲在容器(container)中,並有幾種手段來進行管理和
維護。
除了上面提到的vector 成員函數之外,它還有一個reserve()成員函數,用來預訂給定的
向量尺寸。reserve可以減少存取元素時的內存重分配。
2.2 線性表(List)
和向量(vector) 不一樣,線性表(list)不支持對元素的任意存取(即不通過下標[]存取)。list
中提供的成員函數和vector 類似,有begin 、end、rbegin、rend、push_back、pop_back,list 也提供對錶首元素的操作push_front 、pop_front 。兩個線性表可以用splice、merge合併在一起,也可翻轉(reverse)和排序(sort),也可讓表中同值的元素唯一(unique) 。
2.3 雙向隊列(Deque)
象向量vector 一樣,雙向隊列(Deque) 支持任意存取。它提供的成員函數還有 push_front 、pop_front 、insert 、push、earse 、pop 等。
2.4 關聯容器(Container)
關聯容器(Associative Container) 提供了快速檢索基於關鍵詞(Key)的數據的能力。和序列容器(vector 、list、deque)一樣,關聯容器用來存儲數據,而且設計關聯容器時考慮到了優化數據檢索的意圖 ---通過關鍵詞(Key)作爲標識把單一的數據記錄組織到特定的結構中(如tree)。STL 提供了不同的關聯容器:集合(set)、多元集合(multiset) 、映射(map) 、多元映射(multimap)。
set 和map支持唯一關鍵詞(unique key) ,就是對每個KEY,最多隻保存一個元素(數據記錄)。multiset和multimap 則支持相同關鍵詞(equal key) ,這樣可有很多個元素可以用同一個KEY 進行存儲。set(multiset) 和map(multimap) 之間的區別在於set(multiset) 中的存儲數據內含了KEY 表達式;而map(multimap)則將Key 表達式和對應的數據分開存放。
假設現在要保存某公司裏僱員的信息。僱員信息類定義如下:
class employee_data
{
public:
employee_data() : name(“”), skill(0),salary(0) {}
employee_data(string n, int s, long sa) :name(n), skill(s), salary(sa) {}
stirng name; // 僱員名字
int skill; // 僱員職稱
long salary; // 僱員薪水
friend ostream&operator<<(ostream& os, const employee_data& e);
};
ostream& operator<<(ostream&os, const employee_data& e)
{
os << “employee: “ << e.name<< “ “ << e.skill << “ “ << e.salary;
return os;
}
現在想把僱員數據保存在集合set(multiset) 中,關鍵詞KEY 包含在被保存的對象中:
class employee
{
public:
employee(int ii, const employee_data& e): identification_code(i), description(e) {}
intidentification_code; // 標識僱員的關鍵詞
employee_data description;
bool operator<(const employee& e)const
{
return identification_code <e.identification_code;
}
};
現在我們聲明僱員集合set(multiset):
set<employee, less<employee>>employee_set;
multiset<employee,less<employee>> employee_multiset;
此時,employee既是Key type 又是Value type 。
如果我們想把僱員信息保存在映射map(multimap) 中,則如下聲明:
map<int, employee_data,less<int>> employee_map;
multimap<int, employee_data,less<int>> employee_multimap;
此時Keytype 是int ,而Valuetype 是employee_data 。
所有的關聯容器都有以下成員函數:begin, end, rbegin, rend, empty,size, max_size, swap 、insert、erase 等,其意義同順序容器一樣。下面我們用插入函數insert 想關聯容器中加入元素:
employee_data ed1(“john”,1, 5000); // 僱員John 的信息
employee_data ed2(“tom”,5, 2000); // 僱員tom 的信息
employee_data ed3(“mary”,2, 3000); // 僱員mary 的信息
employee e1(1010, ed1); // 僱員John,證件號1010(KEY)
employee e2(2020, ed2); // 僱員tom ,證件號2020(KEY)
employee e3(3030, ed3); // 僱員mary ,證件號3030(KEY)
employee_set.insert(e1); // 第一次,成功加入僱員John 的信息
employee_set.insert(e1); // 第二次,不成功加入僱員John 的信息。因爲已經存在了
假定John 和Tom 在同一部門(部門代號:101) 工作,而Mary在另一部門工作(部門代號:102)現在把這三位用multimap 來保存:
employee_multimap.insert(make_pair(101,ed1)); // 101 部門的John
employee_multimap.insert(make_pair(101,ed2)); // 101 部門的Tom
employee_multimap.insert(make_pair(102,ed3)); // 102 部門的Mary
那麼現在在101部門工作的僱員就有2 人了:
multimap<int, employee_data,less<int>>::size_type count = employee_multimap.count(101);
有關關聯容器成員函數的詳細定義請參考STL 源碼。
3.迭代器(Iterator)
爲了將算法應用到容器中的元素上,下面要對迭代器(Iterator)做進一步的解釋。
迭代器(Iterator)是指針(pointer) 的泛化,它允許程序員以相同的方式處理不同的數據結構(容器)。STL 中有5中類型的迭代器:任意存取迭代器、雙向迭代器、向前迭代器、輸入迭代器、輸出迭代器。它們分別滿足一定的要求。不同的迭代器要求定義的操作不一樣。比如某個算法需要一個雙向迭代器(Bidirctional Iterator) ,你可以把一個任意存取迭代器(Random Access Iterator) 作爲參數;但反之不行。
3.1 輸入和輸出迭代器
輸入迭代器(Input Iterator) 需要滿足的條件:
·constructor
·assignment operator
·equality/inequality operator
·dereferenc operator
·pre/post increment operator
輸入迭代器表示要從其中取出一個值,即從輸入迭代器X 中取值只可有以下三種形式:
V = *X++
V = *X, ++X
V = *X, X++
輸出迭代器(Output Iterator)需要滿足的條件:
·constructor
·assignment operator
·dereferenc operator
·pre/post increment operator
一個輸出迭代器X 只能有一個值V 存儲其中,在下一個存儲之前,必須將X 加一。即只可有以下三種形式之一:
*X++=V
*X = V, ++X
*X = V, X++
注意,對輸入和輸出迭代器,一旦取出或放入值後,就只能前進(increment),不可多次取出
或放入。
3.2 向前迭代器
向前迭代器(Forward Iterator)需要滿足的條件:
·constructor
·assignment operator
·equality/inequality operator
·dereferenc operator
·pre/post increment operator
和輸入和輸出迭代器比較而言,兩個向前迭代器r 和s,若r == s 則++r== ++s 。而且既可取值,也可賦值:*X = V,V = *X 。看一個例子:
template <class ForwardIterator, classT>
ForwardIterator find_linear(ForwardIteratorfirst, ForwardIterator last, T& value)
{
while(first != last)
if(*first++ == value)
returnfirst;
return last;
}
函數find_linear()在一個容器中循環,如果找到指定的值,則返回該值的迭代器位置;否則返回past-the-end 迭代器。下面用這個函數:
vector<int> v(3, 1);
v.push_back(7); // vector v: 1 1 1 7
vector<int>::iterator i =find_linear(v.begin(), v.end(), 7);
if (i != v.end())
cout<< *i;
else
cout<< “NOT FOUND”;
輸出結果:7
3.3 雙向迭代器
在向前迭代器的基礎上,雙向迭代器(Bidirectional Iterator) 還滿足以下需求:
·pre/post decrement operator
即雙向迭代器不僅允許++,而且允許--。它允許一個算法走過(pass through) 容器中的元素時,即可想前走,也可向後走。
我們看一個使用雙向迭代器的多遍走過(multi-pass) 算法 ---冒 泡排序(Bubble Sort):
template <class BidirectionalIterator,class Compare>
void bubble_sort(BidirectionalIterator first,BidirectionalIterator last, Compare comp)
{
BidirectionalIteratorleft_el = first, right_el = first;
right_el++;
while(first != last)
{
while(right_el != last)
{
if(comp(*right_el, *left_el))
iter_swap(left_el, right_el);
right_el++;
left_el++;
}
last--;
left_el= first;
right_el= first;
right_el++;
}
}
二元函數對象Compare 由用戶提供,得出兩個參數的斷言結果(true/false)。用法:
list<int> l; // list 類似於vector ,但不支持下標[]存取
// 填充list
bubble_sort(l.begin(), l.end(),less<int>()); // 遞增排序
bubble_sort(l.begin(), l.end(),greater<int>()); // 遞減排序
3.4 任意存取迭代器
在雙向迭代器的基礎上,任意存取迭代器(Random Access Iterator) 還滿足以下需求:
·opetrator+(int)
·opetrator+=(int)
·opetrator-(int)
·opetrator-=(int)
·opetrator-(random access iterator)
·opetrator[](int)
·opetrator>(random accessiterator)
·opetrator<(random accessiterator)
·opetrator>=(random accessiterator)
·opetrator<=(random accessiterator)
也就是說,任意存取迭代器可以“任意的”前進和後退。
3.5 迭代標籤(IteratorTag)
每個迭代器(iterator)必須定義表達式iterator_tag(),它返回該迭代器的類別標籤(category
tag)。
STL 中有5個迭代器標籤:input_iterator_tag 、output_iterator_tag 、forward_iterator_tag、
bidirectional_iterator_tag 、random_access_iterator_tag 。
迭代器標籤被用在編譯時選擇效率最高的算法(因爲幾乎所有的算法都用到迭代器)。
4. 算法和函數對象
STL庫中的算法都以迭代器類型爲參數,這就和數據結果的具體實現分離開了。基於此,這些算法被稱作泛型算法(generic algorithm) 。
4.1 如何創建泛型算法
這裏給出一個二分搜索的基本算法(generic bianry search algorithm) ,可以看看基本算法
的實現思路。給定一個排好序的整數數組,要求二分搜索某個值的位置:
const int* binary_search(const int* array,int n, int x)
{
const int* lo = array, *hi = array+n, *mid;
while (lo != hi)
{
mid =lo+(hi-lo)/2;
if (x == *mid)
return mid;
if (x < *mid)
hi = mid;
else
lo = mid+1;
}
return 0;
}
爲了讓上述算法對任意類型的整數數組起作用,下面將之定義成模板函數:
template <class T>
const T* binary_search(const T* array, int n,const T& x)
{
const T* lo = array, *hi = array+n, *mid;
while (lo != hi)
{
mid =lo+(hi-lo)/2;
if (x == *mid)
return mid;
if (x < *mid)
hi = mid;
else
lo = mid+1;
}
return 0;
}
在上面的算法中,如果沒有找到指定的值,則一個特殊的指針NULL (0) 被返回了,這就要求這個值存在。我們不想做這樣的假定這樣的值,可以不成功的搜索返回指針array+n (就是past-the-end) 來代替:
template <class T>
const T* binary_search(const T* array, int n,const T& x)
{
const T* lo = array, *hi = array+n, *mid;
while (lo != hi)
{
mid =lo+(hi-lo)/2;
if (x == *mid)
return mid;
if (x < *mid)
hi = mid;
else
lo = mid+1;
}
return array+n;
}
爲了代替給定數組及尺寸,我們可以給定第一個及past-the-end 元素的指針:
template <class T>
const T* binary_search(T* first, T* last,const T& value)
{
const T* lo = first, *hi = last, *mid;
while (lo != hi)
{
mid = lo+(hi-lo)/2;
if (value == *mid)
return mid;
if (value < *mid)
hi = mid;
else
lo = mid+1;
}
return last;
}
現在可以看出,first 和last 兩個指針是任意類型的指針,即它們和類型T 是沒有關係的。這時就用到了迭代器(Iterator) 的概念,因爲這裏排序要對容器任意存取,我們把first 和last 的類型命名爲“RandomAccessIterator”。代碼改寫如下:
template <class RandomAccessIterator,class T>
RandomAccessIteratorbinary_search(RandomAccessIterator first, RandomAccessIterator last,
const T& value)
{
RandomAccessIterator not_found = last, mid;
while (first != last)
{
mid = first+(last-first)/2;
if (value == *mid)
return mid;
if (value < *mid)
last = mid;
else
first = mid+1;
}
return not_found;
}
上面這個基本二分搜索算法即使對內部類型(build in types) 功能也一樣:
int x[10]; // 10 個整數數組
int search_value; // 太搜索的值
// 初始化變量
int* i = binary_search(&x[0], &x[10],search_value);
if (i == &x[10])
cout << “value NOT FOUND”;
else
cout << “value found”;
所有STL 中的算法都是用以上類似的辦法定義的。
4.2 STL 算法
STL中的算法可以分成四組:組(Group) 算法類型(Algorithm Type)
1不改變順序的操作(Non-mutating sequence Operations)
2改變順序的操作(Mutating sequence Operations)
3排序及相關操作(Sorting and related Operations)
4常用的數字操作(Generalized numeric Operations)
Group1中的操作不改變容器中元素的順序,而Group2 中的要改變。當然Group3 排序操作也會改變元素的順序,但把排序相關的操作獨立於Group1 列出來了。Group4中是些常用的數字操作。
先看個Group1 中的一個算法“對每個(for_each)”,有三個參數,兩個輸入迭代器,一
個函數對象。這個操作的意思是對[first, last)的每個元素都做一個Function 操作:
template <class InputIterator, classFunction>
Function for_each(InputIterator first,InputIterator last, Function f) {
while (first != last) f(*first++);
return f;
}
下面給一個使用for_each 的例子:
template <class T>
class sum_up
{
public:
void operator() (const T& value) { sum +=value; }
const T& read_sum() { return sum; }
private:
static T sum;
};
int sum_up<int>::sum = 0;
void main()
{
deque<int> d(3, 2); // 兩個元素:3 3
sum_up<int> s;
for_each(d.begin(), d.end(), s);
cout << s.read_sum();
}
輸出結果:6。注意到這裏用到了函數對象sum_up,它定義了operator 。所以函數對象是STL 中一個非常重要的概念,屢屢用到。
Group1 中還有其他的操作,如“尋找(Find)”、“鄰居尋找(Adjacent find)”、“計數
(Count)”、“不匹配(Mismatch)”、“相等(Equal)”、“搜索(Search)”等。
說明,如果某個算法的後綴是_if,表示它自己提供斷言(Predicate) 函數,比如find 算法
有find_if的變種。
Group2中的有算法“拷貝(Copy)”、“交換(Swap)”、“變換(Transform)”、“替換
(Replace)”、“填充(Fill)”、“產生(Generate)”、“遷移(Remove)”、“唯一(Unique)”、“翻轉(Reverse)”、“旋轉(Rotate)”、“任意洗牌(Random shuffle)”、“分區(Partitions)”。
說明,如果某個算法的後綴有_copy,表示它要把一個迭代器區間的內容拷貝到另一個
迭代器中。比如replace 有replace_copy 的變種。
Group3 中的排序算法都有兩個版本,一個用函數對象做比較,一個用operator< 做比較。比如算法“排序(sort)”有兩個版本:
void sort(RandomAccessIterator first,RandomAccessIterator last);
void sort(RandomAccessIterator first,RandomAccessIterator last, Compare comp) ;
Group3中還有算法“第N 個元素(Nthelement)”、“二分搜索(Binary Search)”、“合併
(Merge)”、“排好序的設置操作(Set operations on sorted structures)”、“堆操作(Heap Operations)
”、“最大最小(Minimum and Maximum)”、“詞典比較(Lexicographical comparison)”、“置換產生器(Permutation generator)”。
Group4 中包含些常用的數字算法,比如“聚集(Accumulate)”、“內部乘積(Innerproduct)”
、“局部和(Partialsum)”、“鄰近不同(Adjacent difference)”。
5. 適配器(Adaptor)
適應器(Adaptor) 是提供接口映射的模板類(Adaptors are template classes that provide
interface mappings) 。適應器基於其他類來實現新的功能,成員函數可以被添加、隱藏,也可合併以得到新的功能。
5.1容器適配器
棧(Stack)
棧可以用向量(vector) 、線性表(list)或雙向隊列(deque)來實現:
stack<vector<int>> s1;
stack<list<int> > s2;
stack<deque<int>> s3;
其成員函數有“判空(empty)”、“尺寸(Size)”、“棧頂元素(top)”、“壓棧(push)”、“彈棧(pop)”等。
隊列(Queue)
隊列可以用線性表(list)或雙向隊列(deque) 來實現(注意vector container 不能用來實現
queue ,因爲vector沒有成員函數pop_front!):
queue<list<int>> q1;
queue<deque<int>> q2;
其成員函數有“判空(empty)”、“尺寸(Size)”、“首元(front)”、“尾元(backt)”、“加入隊列(push)”
、“彈出隊列(pop)”等操作。
優先級隊列(Priority Queue)
優先級隊列可以用向量(vector)或雙向隊列(deque)來實現(注意list container 不能用來實
現queue,因爲list 的迭代器不是任意存取iterator ,而pop中用到堆排序時是要求random
access iterator 的!):
priority_queue<vector<int>,less<int>> pq1; // 使用遞增less<int>函數對象排序
priority_queue<deque<int>,greater<int>> pq2; // 使用遞減greater<int>函數對象排序
其成員函數有“判空(empty)”、“尺寸(Size)”、“棧頂元素(top)”、“壓棧(push)”、“彈棧(pop)”等。
5.2迭代適配器
逆向迭代器(Reverse Iterator)
顧名思義,逆向迭代器的遞增是朝反方向前進的。對於順序容器(vector, list 和deque)
,其成員函數rbegin()和rend()都返回了相應的逆向迭代器:
list<int> l;
for (int i = 1; i < 5; i++)
l.push_back(i);
copy(l.rbegin(), l.rend(),ostream_iterator<int> (cout, “ “));
輸出結果是:4 3 2 1
插入迭代器(Insert Iterator)
插入迭代器簡化了向容器中插入元素的工作。插入迭代器指定了向容器中插入元素的位
置。STL中有三種插入迭代器:
·向後插入(back_insert_iterator) ,在容器尾部插入
·向前插入(front_insert_iterator) ,在容器頭部插入
·插入(insert_iterator) ,在容器中任一位置
STL中提供了三個函數分別構造相應的插入迭代器:
·back_inserter
·front_inserter
·inserter
原始存儲迭代器(Raw Storage Iterator)
該迭代器允許算法將其結果保存進沒有初始化的內存中。
5.3函數適配器
否認者(Negator)
有兩個否認者not1 和not2 分別是一元和二元函數,它們分別使用一元和二元的謂詞
(Predicate) ,返回的是謂詞的結果的取反。
綁定者(Binder)
STL中有兩個綁定者函數bind1st 和bind2nd ,可以將一些值限定在指定區間中。
函數指針的適配器(Adaptors for pointers tofunction)
STL 中的算法和容器一般都需要函數對象(function object) 作爲參數。如果想用到常用的C++函數時,可以用ptr_fun 把普通函數轉換成函數對象。比如ptr_fun(strcmp) 就把常用的串比較函數strcmp 包裝成一個函數對象,就可用在STL 算法和容器中了。
6 其餘的STL部件
(1)分配算符和內存處理
可移植性的一個主要問題是能夠把有關內存模型的信息封裝起來。這些信息有:
·指針類型
·指針差異的類型(ptrdiff_t)
·內存模型中對象尺寸的類型(size_t)
·內存分配和回收原語
STL中提供的分配算符(allocator) 對象封裝了以上信息。STL 中的容器(container)都有一個alllocator 參數,這樣容器就不用關心內存模型信息了。
STL中提供了缺省的allocator 對象,各家編譯器也提供其產品支持的不同的內存模型
allocator 。對每一種內存模型都要提供以下幾個模板函數:
·allocate :分配緩衝區
·deallocate :回收緩衝區
·construct :通過調用合適的拷貝構造函數將結果直接放入沒有初始化的內存中
·destroy:調用指定指針的析構函數
(2) 各部件如何協同工作
STL的容器(contianer) 用來存儲任意類型的對象。容器需要分配算符(allocator) 爲參數。
分配算符(allocator) 是能夠封裝所使用內存模型信息的對象,它提供內存原語以對內存進行統一的存取。每種內存模型都有自己特定的allocator 。Container 使用allocator完成對內存的操作,內存模型的改變隻影響allocator ,而不會(在代碼一級)影響contianer 對象。
算法(algorithm) 是計算順序。兩個算法不同在其本身的計算上,而非讀取輸入數據和寫出輸出數據的方法上。STL 爲算法提供了統一的數據存取機制 ---迭代器(iterator)。不同的迭代器提供不同存取方式。
函數對象(function object) 用在和算法的結合中,用以擴展算法的效用。
適應器(adaptor) 是接口映射,它們在基本或已有的部件上實現新的對象,以提供不同或擴展的能力。
在設計STL 時,不同部件間的接口都定義的儘可能少。STL 的目的在於:
·簡化應用程序的設計
·減少要寫的代碼函數
·增加可理解度和可維護性
·提供基本的質量保證
實際上STL 中有的功能在MFC 中都有替代品。例如在MFC 中有CArray等。不過,在你只能使用標準C/C++ 如在Unix 或Linux環境下要使用這些數據結構,使用STL 是一個很好的選擇。STL 最大的特點是它使用的算法非常可靠且效率很高,例如其中的二叉樹算法就很經典。不過看 STL 源代碼還是有點困難的,需要下點功夫。不過如果只是要使用可以不看源代碼,使用起來很簡單的。