泛型程式設計(容器、迭代器、介面卡)
面向物件程式設計關注的是程式設計的資料方面,而泛型程式設計關注的是演算法。
迭代器(iterator)
模板使得演算法獨立於儲存的資料型別,而迭代器使演算法獨立於使用的容器型別。(演算法引數變為迭代器型別,而不是各種類物件,函式物件,型別引數?)
迭代器不是某種型別,而是一系列的要求。可能需要設計一個迭代器類來滿足需要達到的要求,可能只需要常規指標就能達到要求。(迭代器可以是指標,也可以是物件)
每個容器類(vector、list、deque等)定義了相應的迭代器型別。對於其中的某個類,迭代器可能是指標;而對於另一個類,則可能是物件。
STL文獻使用術語概念(concept)
概念可以具有類似繼承的關係(輸入出迭代器、正向迭代器、雙向迭代器等),例如雙向迭代器繼承了正向迭代器的功能。然而,
不能將C++繼承機制用於迭代器。例如,可以將正向迭代器實現為一個類,而將雙向迭代器實現為一個常規指標。對C++而言,這種雙向迭代器是一種內建型別(指標型別),不能從類派生而來。然而,從概念上來看,它確實能夠繼承。
有些STL文獻使用術語改進(refinement)來辨識這種概念上的繼承。
概念的具體實現被稱為模型(model)。
迭代器是廣義的指標,而指標滿足所有的迭代器要求。
迭代器是STL演算法的介面,而指標是迭代器,因此STL演算法可以使用指標來對基於指標的非STL容器進行操作。
#include "iostream"
#include "algorithm"
using namespace std;
int main()
{
int arr[5] = {5,2,3,4,5};
sort(arr, arr + 5);
for (int x : arr)
cout << x << " ";
cout << endl;
return 0;
}
由於指標是迭代器,而演算法是基於迭代器的,這使得STL演算法可以用於常規陣列。同樣,可以將STL演算法用於自己設計的陣列形式,只要提供適當的迭代器(可以是指標,也可以是物件)和超尾指示器即可。
介面卡(adapter)
介面卡模式(Adapter Pattern)是一種補救模式,將一個類的介面轉換成客戶希望的另外一個介面,從而使原本由於介面不相容而不能一起工作的類可以一起工作。
容器介面卡:改變容器介面。
STL提供兩個容器迭代器:queue和stack。它們都是修飾deque後成為另一種風貌的容器。
迭代器介面卡:STL為某種迭代器提供了模板,該模板是迭代器概念的一個模型(概念的具體實現),它也是一個介面卡(一個類或函式)。可以將一些其他介面轉換為STL使用的介面。
有時,一個類將具有您所尋求的功能,但不具有訪問該功能的正確介面。例如,STL copy()演算法需要一對輸入迭代器作為其前兩個引數,以給出要複製的值範圍。 istream物件可以充當此類資料值的源,但它沒有複製演算法可以使用的任何迭代器。
類似地,copy()演算法的第三個引數是一個輸出迭代器,它將複製的值定向到正確的目標。該目標可以是輸出流,但輸出流不直接提供任何輸出迭代器。
通過“調整”要傳送的訊息來生成其他類物件想要接收的訊息,介面卡類就像“翻譯器”一樣。
有一個名為istream_iterator的迭代器介面卡類,它提供了copy()演算法對輸入所期望的介面,並將來自此演算法的請求轉換為適當的istream操作。另外還有一個名為ostream_iterator,它提供了copy()演算法對輸出所期望的介面,並將來自演算法的請求轉換為適當的ostream操作。
#include "iostream"
#include "string"
#include "vector"
#include "iterator"
#include "algorithm"
using namespace std;
void Show(int n)
{
cout << n << " ";
}
int main()
{
int cast[5] = { 1,2,3,4,5 };
vector<int> dice(5);
copy(cast, cast + 5, dice.begin());
for(int x: dice)
cout << x << " ";
cout << endl;
vector<int>::iterator ri;
for (ri = dice.begin(); ri != dice.end(); ++ri)
cout << *ri << " ";
cout << endl;
ostream_iterator<int, char> out_iter(cout, " ");
copy(dice.begin(), dice.end(), out_iter); //將dice容器的整個區間複製到輸出流
cout << endl;
vector<int>::reverse_iterator ro;
for (ro = dice.rbegin(); ro != dice.rend(); ++ro)
cout << *ro << " ";
cout << endl;
for_each(dice.rbegin(), dice.rend(), Show);
cout << endl;
return 0;
}
/*out_iter物件建構函式的第一個引數cout指出了要使用的輸出流,它也可以是用於檔案輸出的流。
後一個字串引數是在傳送給輸出流的每個資料項後顯示的分隔符*/
copy()的前兩個迭代器引數表示要複製的範圍,最後一個迭代器引數表示要將第一個元素複製到什麼位置。前兩個引數必須是(或最好是)輸入迭代器,最後一個引數必須是(或最好是)輸出迭代器。
這裡使用ostream_iterator模板(介面卡),將其他介面轉換為STL使用的介面。程式碼中現在out_iter迭代器是一個介面,讓你使用cout來顯示資訊。
/*也可以不建立命名的迭代器,而直接構建一個匿名的迭代器。即可以這樣使用介面卡*/
copy(dice.begin(),dice.end(),ostream_iterator<int,char>(cout," "));
再舉個例子參考
- Insert Iterator:將容器繫結到back_insert_iterator、front_insert_iterator、insert_iterator。它們都是一個類,對它們的賦值操作將轉換為對繫結容器的插入操作。為了操作方便,向用戶提供的是一個函式,函式中才建立上述類。
以back_inserter()函式為例:
template <class Container>
inline back_insert_iterator<Container> back_inserter(Container& x) {
return back_insert_iterator<Container>(x); // 以容器為引數,建立迭代器介面卡
}
注意,一般迭代器介面卡不會以迭代器作為引數,這裡通過傳入一個容器建立一個迭代器介面卡。
下面是back_insert_iterator類的定義:
template <class Container>
class back_insert_iterator {
protected:
Container* container; // 注意,這裡是一個指向容器的指標
public:
typedef output_iterator_tag iterator_category; // 輸出迭代器,只支援自增
typedef void value_type;
typedef void difference_type;
typedef void pointer;
typedef void reference;
explicit back_insert_iterator(Container& x) : container(&x) {} // 與容器相繫結
back_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_back(value);
return *this;
}
back_insert_iterator<Container>& operator*() { return *this; }
back_insert_iterator<Container>& operator++() { return *this; }
back_insert_iterator<Container>& operator++(int) { return *this; }
};
可以看到,迭代器介面卡提供了自增和接引用的介面,但是實際的功能被關閉了。上述程式碼的關鍵在於,對迭代器介面卡的賦值變為了對容器的插入操作。
下面是作者寫的一個類似於上面的迭代器介面卡:
#include <iostream>
#include <vector>
using namespace std;
template <class Container>
class my_back_insert_iterator {
protected:
Container* container;
public:
explicit my_back_insert_iterator(Container& x) : container(&x) {}
my_back_insert_iterator<Container>&
operator=(const typename Container::value_type& value) {
container->push_back(value);
return *this;
}
};
int main()
{
vector<int> vec;
my_back_insert_iterator< vector<int> > back_insert(vec);
back_insert = 1;
back_insert = 2;
back_insert = 3;
back_insert = 4;
vector<int>::iterator iter = vec.begin();
for ( ; iter != vec.end(); ++iter)
cout << *iter << endl;
return 0;
}
容器種類
STL具有容器概念和容器型別。
概念是具有名稱(如容器、序列容器、關聯容器)的通用類別。(大類)
容器型別是可用於建立具體容器物件的模板(deque、list、queue、stack、vector、map等)。(小類)