1. 程式人生 > >C++ 泛型基礎

C++ 泛型基礎

泛型的基本思想:
泛型程式設計(Generic Programming)是一種語言機制,通過它可以實現一個標準的容器庫。
像類一樣,泛型也是一種抽象資料型別,但是泛型不屬於面向物件,它是面向物件的補充和發展。
在面向物件程式設計中,當演算法與資料型別有關時,面向物件在對演算法的抽象描述方面存在一些缺陷。
比如對棧的描述:
class stack
{

   push(引數型別)  //入棧演算法

   pop(引數型別)   //出棧演算法

}
如果把上面的虛擬碼看作演算法描述,沒問題,因為演算法與引數型別無關。但是如果把它寫成可編譯的原始碼,
就必須指明是什麼型別,否則是無法通過編譯的。使用過載來解決這個問題,即對N種不同的引數型別寫N個
push和pop演算法,這樣是很麻煩的,程式碼也無法通用。
若對上面的描述進行改造如下:
首先指定一種通用型別T,不具體指明是哪一種型別。
class stack<引數模板 T>
{

   push(T)  //入棧演算法

   pop(T)   //出棧演算法

}

這裡的引數模板T相當於一個佔位符,當我們例項化類stack時,T會被具體的資料型別替換掉。
若定義物件S為statc型別,在例項化S時若我們將T指定int型則:
這時候類S就成為:
class S
{
    push(int)  //入棧演算法
    pop(int)   //出棧演算法
}
這時我可以稱class stack<引數模板 T>是類的類,通過它可以生成具體引數型別不同的類。
泛型在C++中的應用:
泛型在C++中的主要實現為模板函式和模板類。
通常使用普通的函式實現一個與資料型別有關的演算法是很繁瑣的,比如兩個數的加法,要
考慮很多型別:
int add(int a,int b) { return a+b; }
float add(float a,float b) { return  a+b; }
。。。。
雖然在C++中可以通過函式過載來解決這個問題,但是反覆寫相同演算法的函式是比較辛苦的,
更重要的是函式過載是靜態編譯,執行時佔用過多記憶體。
在此我們可以用C++的模板函式來表達通用型的函式,如下:
template<typename T> // 模板宣告
T add(T a,T b) { return a+b; }  // 注意形參和返回值的型別
這時C++編譯器會根據add函式的引數型別來生成一個與之對應的帶具體引數型別的函式並
呼叫。
例如:
#include <iostream>
using namespace std;
template <typename T>
T add(T a,T b)  //注意形參和返回型別
{  
 return a+b;
}
void main()
{
    int num1, num2, sum;
    cin>>num1>>num2;
    sum=add(num1,num2); //用int匹配模版引數T,若sum,num1,num2型別不一致則無法匹配。
    cout<<sum;
}
函式模板的性質

1) 函式模板並不是真正的函式,它只是C++編譯生成具體函式的一個模子。
2) 函式模板本身並不生成函式,實際生成的函式是替換函式模板的那個函式,比如上例中的add(sum1,sum2),
    這種替換是編譯期就繫結的。
3) 函式模板不是隻編譯一份滿足多重需要,而是為每一種替換它的函式編譯一份。
4) 函式模板不允許自動型別轉換。
5) 函式模板不可以設定預設模板實參。比如template <typename T=0>不可以。
C++模版函式的語法
template  <typename 模版引數列表…>
函式返回型別 函式名(形參列表…)
上面兩行可以合併成一行。
例如:
下面的幾種寫法是等效的並且class 和typename是可以互換的。
template  <typename T1, typename T2>
T1 fun(T1, T2, int )
{  //…..}
template  <typename T1,T2>  T1 fun(T1, T2, int )
{  //…..}
template  <class T1, class T2>
 T1 fun(T1, T2, int )
{  //…..}
template  <class T1,T2>  T1 fun(T1, T2, int )
{  //…..}
C++模版類的語法
template  <class 模版引數列表…>
class 類名
{ //類體}
成員的實現…
例如:
//類宣告部分,有兩個模板引數T1,T2
template  <class T1, class T2 >  
class A {
   private:
   int a;
  T1 b;  //成員變數也可以用模板引數
  public: 
  int fun1(T1 x, int y );
 T2 fun2(T1 x, T2 y);
}
//類實現部分
template  <class T1, class T2 >
int A<T1>:: fun1(T1 x, int y ){//實現…… }
 template  <class T1, class T2 >
T2 A<T1,T2>:: fun2(T1 x, T2 y) {//實現…… }
 //使用類A
 int main( ) {
 //定義物件a,並用int替換T1, float替換T2
   A<int, float>  a;
   //例項化a,呼叫a的屬性和方法……
}
由上例可以看出, 類模板引數T1,T2對類的成員變數和成員函式均有效。
在C++程式設計中,當你要實現的一個類的某些成員函式和成員變數的演算法
資料型別有關,可以考慮用類模板。C++版的資料結構演算法大都用類模板實現。
類模板的性質
1) 類模板不是真正的類,它只是C++編譯器生成具體類的一個模子。
2) 類模板可以設定預設模板實參。
C++ STL簡介
  STL(Standard Template Library,標準模板庫)是C++對泛型程式設計思想的實現,最早是惠普實驗室開發的。
在被引入C++之前該技術就已經存在了很長的一段時間。後來STL成為ANSI/ISO C++標準的一部分。各個
C++廠商也有各自相應的模板庫,這些庫效率可能很高,但可移植性不一定好。
  STL廣義上分為三類:algorithm(演算法)、container(容器)和iterator(迭代器),幾乎所有的程式碼都採
用了模板類和模板函式的方式,這相比於傳統的由函式和類組成的庫來說提供了更好的程式碼重用機會。
  在C++標準中,STL被組織為下面的13個頭檔案:<algorithm>、<deque>、<functional>、<iterator>、<vector>、
<list>、<map>、<memory>、<numeric>、<queue>、<set>、<stack> 和<utility>。
1) 演算法(algorithm)
  STL提供了大約100個實現演算法的模版函式,演算法部分主要由標頭檔案<algorithm>,<numeric> 和<functional>組成。
  <algorithm>是所有STL標頭檔案中最大的一個,它是由一大堆模板函式組成的,其中常用到的功能範圍涉及到比較、
交換、查詢、遍歷操作、複製、修改、移除、反轉、排序、合併等等。
  <numeric>體積很小,只包括一些簡單數學運算的模板函式。
  <functional>中則定義了一些模板類,用以宣告函式物件。
2)  容器(container)(又稱集合collection)
  在實際的開發過程中,資料結構本身的重要性不會遜於操作於資料結構的演算法的重要性,當程式中存在著對時間要
求很高的部分時,資料結構的選擇就顯得更加重要。
  通過設定一些模版類,STL容器對最常用的資料結構提供了支援,這些模板的引數允許指定容器中元素的資料類
型,可以將許多重複而乏味的工作簡化。
如下表:

資料結構

實現標頭檔案

向量(vector)

順序性容器

<vector>

列表(list)

順序性容器

<list>

雙佇列(deque)

順序性容器

<deque>

集合(set)

關聯容器

<set>

多重集合(multiset)

關聯容器

<set>

棧(stack)

容器介面卡

<stack>

佇列(queue)

容器介面卡

<queue>

優先佇列(priority_queue)

容器介面卡

<queue>

對映(map)

關聯容器

<map>

多重對映(multimap)

關聯容器

<map>


3)迭代器(iterator)
  迭代器是一種允許程式設計師檢查容器內元素,並實現元素遍歷的資料型別。C++標準庫為每一種標準容器定義了一種迭代器型別。迭代器型別提供了比下標操作更一般化的方法:所有的標準庫容器都定義了相應的迭代器型別,而只有少數的容器(比如陣列)支援下標操作。因為迭代器對所有的容器都適用,現代C++程式更傾向於使用迭代器而不是下標操作訪問容器元素。
  迭代器從作用上來說是STL最基本的部分,迭代器在STL中用來將演算法和容器聯絡起來,起著一種黏和劑的作用。幾乎STL提供的所有演算法都是通過迭代器存取元素序列進行工作的,每一個容器都定義了其本身所專有的迭代器,用以存取容器中的元素。
  迭代器部分主要由標頭檔案<utility>,<iterator> 和<memory>組成。<utility>是一個很小的標頭檔案,它包括了貫穿使用在STL中的幾個模板的宣告,<iterator>中提供了迭代器使用的許多方法, <memory>為容器中的元素分配儲存空間,同時也為某些演算法執行期間產生的臨時物件提供機制,<memory>中的主要部分是模板類allocator,它負責產生所有容器中的預設分配器。