VC++-標準模板庫STL(Standard Template Library)

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 源代碼還是有點困難的,需要下點功夫。不過如果只是要使用可以不看源代碼,使用起來很簡單的。

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