關於C++的模板操作

看了一篇關於模板的文章, 寫的不錯, 只是在編譯的時候有點小問題做了修改, 添加了一些補充性的概念, 以便方便我更加理解模板的概念,感謝原作者提供的好文章, 原文地址:http://prglab.com/cms/pages/c-tutorial/advanced-concepts/templates.php

輔助文章可以參考:http://www.kuqin.com/language/20090405/44193.html

 

何時需要模板:

我的理解是(不知道對不對) 一般需要函數通用或者類通用的時候需要模板, 還有就是泛型編程, 什麼是泛型編程呢?

網上找了篇文章 呵呵

 

泛型編程

  泛型編 程讓你編寫完全一般化並可重複使用的算法,其效率與針對某特定數據類型而設計的算法相同。泛型編程的代表作品STL是一種高效、泛型、可交互操作的軟件組 件。所謂泛型(Genericity),是指具有在多種數據類型上皆可操作的含意,與模板有些相似。STL巨大,而且可以擴充,它包含很多計算機基本算法 和數據結構,而且將算法與數據結構完全分離,其中算法是泛型的,不與任何特定數據結構或對象類型系在一起。STL以迭代器 (Iterators)和容器(Containers)爲基礎,是一種泛型算法(Generic Algorithms)庫,容器的存在使這些算法有東西可以操作。STL包含各種泛型算法(algorithms)、泛型指針(iterators)、泛 型容器(containers)以及函數對象(function objects)。STL並非只是一些有用組件的集合,它是描述軟件組件抽象需求條件的一個正規而有條理的架構。   泛型的第一個好處是編譯時的嚴格類型檢查。這是集合框架最重要的特點。此外,泛型消除了絕大多數的類型轉換。如果沒有泛型,當你使用集合框架時,你不得不進行類型轉換。
  關於泛型的理解可以總結下面的一句話,它是把數據類型作爲一種參數傳遞進來。
函數模板正文:

函數模板( Function templates)

模板(Templates)使得我們可以生成通用的函數,這些函數能夠接受任意數據類型的參數,可返回任意類型的值,而不需要對所有可能的數據類型進行函數重載。這在一定程度上實現了宏(macro)的作用。它們的原型定義可以是下面兩種中的任何一個:

template <class identifier> function_declaration;
template <typename identifier> function_declaration;

上面兩種原型定義的不同之處在關鍵字class 或 typename的使用。它們實際是完全等價的,因爲兩種表達的意思和執行都一模一樣。

例如,要生成一個模板,返回兩個對象中較大的一個,我們可以這樣寫:

 

 

在第一行聲明中,我們已經生成了一個通用數據類型的模板,叫做GenericType。因此在其後面的函數中,GenericType 成爲一個有效的數據類型,它被用來定義了兩個參數a和 b ,並被用作了函數GetMax的返回值類型。

GenericType 仍沒有代表任何具體的數據類型;當函數 GetMax 被調用的時候,我們可以使用任何有效的數據類型來調用它。這個數據類型將被作爲pattern來代替函數中GenericType 出現的地方。用一個類型pattern來調用一個模板的方法如下:

function <type> (parameters);

例如,要調用GetMax 來比較兩個int類型的整數可以這樣寫:

int x,y;
GetMax <int> (x,y);

因此,GetMax 的調用就好像所有的GenericType 出現的地方都用int 來代替一樣。

這裏是一個例子:

 

 

在這個例子中,我們將通用數據類型命名爲T 而不是 GenericType ,因爲T短一些,並且它是模板更爲通用的標示之一,雖然使用任何有效的標示符都是可以的。)

在上面的例子中,我們對同樣的函數GetMax()使用了兩種參數類型:int 和 long,而只寫了一種函數的實現,也就是說我們寫了一個函數的模板,用了兩種不同的pattern來調用它。

如你所見,在我們的模板函數 GetMax() 裏,類型 T 可以被用來聲明新的對象

T result;

result 是一個T類型的對象, 就像a 和 b一樣,也就是說,它們都是同一類型的,這種類型就是當我們調用模板函數時寫在尖括號<> 中的類型。

在這個具體的例子中,通用類型 T 被用作函數GetMax 的參數,不需要說明<int>或 <long>,編譯器也可以自動檢測到傳入的數據類型,因此,我們也可以這樣寫這個例子:

int i,j;
GetMax (i,j);

因爲i 和j 都是int 類型,編譯器會自動假設我們想要函數按照int進行調用。這種暗示的方法更爲有用,併產生同樣的結果:

 

 

注意在這個例子的main() 中我們如何調用模板函數GetMax() 而沒有在括號<>中指明具體數據類型的。編譯器自動決定每一個調用需要什麼數據類型。

因爲我們的模板函數只包括一種數據類型 (class T), 而且它的兩個參數都是同一種類型,我們不能夠用兩個不同類型的參數來調用它:

 

 

上面的調用就是不對的,因爲我們的函數等待的是兩個同種類型的參數。

我們也可以使得模板函數接受兩種或兩種以上類型的數據,例如:

 

在這個例子中,我們的模板函數 GetMin() 接受兩個不同類型的參數,並返回一個與第一個參數同類型的對象。在這種定義下,我們可以這樣調用該函數:

 

 

或者,簡單的用

i = GetMin (j,l);

雖然 j 和 l 是不同的類型。

 

類模板(Class templates)

我們也可以定義類模板(class templates),使得一個類可以有基於通用類型的成員,而不需要在類生成的時候定義具體的數據類型,例如:

 

上面我們定義的類可以用來存儲兩個任意類型的元素。例如,如果我們想要定義該類的一個對象,用來存儲兩個整型數據115 和 36 ,我們可以這樣寫:

mypair<int> myobject (115, 36);

我們同時可以用這個類來生成另一個對象用來存儲任何其他類型數據,例如:

pair<float> myfloats (3.0, 2.18);

在上面的例子中,類的唯一一個成員函數已經被inline 定義。如果我們要在類之外定義它的一個成員函數,我們必須在每一函數前面加template <... >。

以上代碼的模板聲明和實現也可以都放在.h文件中實現

 

 

注意成員函數getmax 是怎樣開始定義的:

template <class T>
T mypair::getmax ()

所有寫 T 的地方都是必需的,每次你定義模板類的成員函數的時候都需要遵循類似的格式(這裏第二個T表示函數返回值的類型,這個根據需要可能會有變化)。

 

模板特化(Template specialization)

模板的特殊化是當模板中的pattern有確定的類型時,模板有一個具體的實現。例如假設我們的類模板pair 包含一個取模計算(module operation)的函數,而我們希望這個函數只有當對象中存儲的數據爲整型(int)的時候才能工作,其他時候,我們需要這個函數總是返回0。這可以通過下面的代碼來實現:

 

關於模板特化, 可參考c++ primer 4th的第16.6章

 

由上面的代碼可以看到,模板特殊化由以下格式定義:

template <> class class_name <type>

這個特殊化本身也是模板定義的一部分,因此,我們必須在該定義開頭寫template <>。而且因爲它確實爲一個具體類型的特殊定義,通用數據類型在這裏不能夠使用,所以第一對尖括號<> 內必須爲空。在類名稱後面,我們必須將這個特殊化中使用的具體數據類型寫在尖括號<>中。

當我們特殊化模板的一個數據類型的時候,同時還必須重新定義類的所有成員的特殊化實現(如果你仔細看上面的例子,會發現我們不得不在特殊化的定義中包含它自己的構造函數 constructor,雖然它與通用模板中的構造函數是一樣的)。這樣做的原因就是特殊化不會繼承通用模板的任何一個成員。

 

模板的參數值(Parameter values for templates)

除了模板參數前面跟關鍵字class 或 typename 表示一個通用類型外,函數模板和類模板還可以包含其它不是代表一個類型的參數,例如代表一個常數,這些通常是基本數據類型的。例如,下面的例子定義了一個用來存儲數組的類模板:

 

 

我們也可以爲模板參數設置默認值,就像爲函數參數設置默認值一樣。

下面是一些模板定義的例子:

 

 

模板與多文件工程 (Templates and multiple-file projects)

從編譯器的角度來看,模板不同於一般的函數或類。它們在需要時才被編譯(compiled on demand),也就是說一個模板的代碼直到需要生成一個對象的時候(instantiation)才被編譯。當需要instantiation的時候,編譯器根據模板爲特定的調用數據類型生成一個特殊的函數。

當工程變得越來越大的時候,程序代碼通常會被分割爲多個源程序文件。在這種情況下,通常接口(interface)和實現(implementation)是分開的。用一個函數庫做例子,接口通常包括所有能被調用的函數的原型定義。它們通常被定義在以.h 爲擴展名的頭文件 (header file) 中;而實現 (函數的定義) 則在獨立的C++代碼文件中。

模板這種類似宏(macro-like) 的功能,對多文件工程有一定的限制:函數或類模板的實現 (定義) 必須與原型聲明在同一個文件中。也就是說我們不能再 將接口(interface)存儲在單獨的頭文件中,而必須將接口和實現放在使用模板的同一個文件中。

回到函數庫的例子,如果我們想要建立一個函數模板的庫,我們不能再使用頭文件(.h) ,取而代之,我們應該生成一個模板文件(template file),將函數模板的接口和實現 都放在這個文件中 (這種文件沒有慣用擴展名,除了不要使用.h擴展名或不要不加任何擴展名)。在一個工程中多次包含同時具有聲明和實現的模板文件並不會產生鏈接錯誤 (linkage errors),因爲它們只有在需要時才被編譯,而兼容模板的編譯器應該已經考慮到這種情況,不會生成重複的代碼。

具體還有其他的模板操作 建議參考 c++primer 第16章

 

 

 

 

 

 

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