1. 程式人生 > >泛型程式設計(容器、迭代器、介面卡)

泛型程式設計(容器、迭代器、介面卡)

面向物件程式設計關注的是程式設計的資料方面,而泛型程式設計關注的是演算法。

迭代器(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等)。(小類)