1. 程式人生 > >c++ 模板詳解(1)

c++ 模板詳解(1)

C++模板

本文轉自:http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html  

模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的引數、返回值取得任意型別。

  模板是一種對型別進行引數化的工具;

  通常有兩種形式:函式模板類模板

  函式模板針對僅引數型別不同的函式

  類模板針對僅資料成員成員函式型別不同的類。

  使用模板的目的就是能夠讓程式設計師編寫與型別無關的程式碼。比如編寫了一個交換兩個整型int 型別的swap函式,這個函式就只能實現int

 型,對double,字元這些型別無法實現,要實現這些型別的交換就要重新編寫另一個swap函式。使用模板的目的就是要讓這程式的實現與型別無關,比如一個swap模板函式,即可以實現int 型,又可以實現double型的交換。模板可以應用於函式和類。下面分別介紹。

  注意:模板的宣告或定義只能在全域性,名稱空間或類範圍內進行。即不能在區域性範圍,函式內進行,比如不能在main函式中宣告或定義一個模板。

 一、函式模板通式

1、函式模板的格式:

    template <class 形參名class 形參名,......返回型別 函式名(引數列表)

   {

      函式體

   }

  其中templateclass是關見字,class可以用typename 關見字代替,在這裡typename 和class沒區別,<>括號中的引數叫模板形參,模板形參和函式形參很相像,模板形參不能為空一但聲明瞭模板函式就可以用模板函式的形參名宣告類中的成員變數和成員函式,即可以在該函式中使用內建型別的地方都可以使用模板形參名。模板形參需要呼叫該模板函式時提供的模板實參來初始化模板形參,一旦編譯器確定了實際的模板實參型別就稱他例項化了函式模板的一個例項。比如swap的模板函式形式為

      template <class T> void swap(T& a, T& b){},

當呼叫這樣的模板函式時型別T就會被被呼叫時的型別所代替,比如swap(a,b)其中abint 型,這時模板函式swap中的形參T就會被int 所代替,模板函式就變為swap(int &a, int &b)。而當swap(c,d)其中cddouble型別時,模板函式會被替換為swap(double &a, double &b),這樣就實現了函式的實現與型別無關的程式碼。

  2、注意:對於函式模板而言不存在 h(int,int) 這樣的呼叫,不能在函式呼叫的引數中指定模板形參的型別,對函式模板的呼叫應使用實參推演來進行,即只能進行 h(2,3) 這樣的呼叫,或者int a, b; h(a,b)

  函式模板的示例演示將在下文中涉及!

二、類模板通式

  1、類模板的格式為:

    template<class  形參名class 形參名>   class 類名

    { ... };

  類模板和函式模板都是以template開始後接模板形參列表組成,模板形參不能為空,一但聲明瞭類模板就可以用類模板的形參名宣告類中的成員變數和成員函式,即可以在類中使用內建型別的地方都可以使用模板形參名來宣告。比如

    template<class T> class A{public: T a; T b; T hy(T c, T &d);};

在類A中聲明瞭兩個型別為T的成員變數ab,還聲明瞭一個返回型別為T帶兩個引數型別為T的函式hy

  2、類模板物件的建立:比如一個模板類A,則使用類模板建立物件的方法為A<int> m;在類A後面跟上一個<>尖括號並在裡面填上相應的型別,這樣的話類A中凡是用到模板形參的地方都會被int 所代替。當類模板有兩個模板形參時建立物件的方法為A<int, double> m;型別之間用逗號隔開。

  3、對於類模板,模板形參的型別必須在類名後的尖括號中明確指定。比如A<2> m;用這種方法把模板形參設定為int是錯誤的(編譯錯誤:error C2079: 'a' uses undefined class 'A<int>'),類模板形參不存在實參推演的問題。也就是說不能把整型值2推演為int 型傳遞給模板形參。要把類模板形參調置為int 型必須這樣指定A<int> m

  4、在類模板外部定義成員函式的方法為:

    template<模板形參列表> 函式返回型別 類名<模板形參名>::函式名(引數列表){函式體},

比如有兩個模板形參T1T2的類A中含有一個void h()函式,則定義該函式的語法為:

    template<class T1,class T2> void A<T1,T2>::h(){}。

注意:當在類外面定義類的成員時template後面的模板形參應與要定義的類的模板形參一致。

  5、再次提醒注意:模板的宣告或定義只能在全域性,名稱空間或類範圍內進行。即不能在區域性範圍,函式內進行,比如不能在main函式中宣告或定義一個模板。

三、模板的形參

有三種類型的模板形參:型別形參,非型別形參和模板形參。

  1、型別形參

    1.1 、型別模板形參:型別形參由關見字class或typename後接說明符構成,如template<class T> void h(T a){};其中T就是一個型別形參,型別形參的名字由使用者自已確定。模板形參表示的是一個未知的型別。模板型別形參可作為型別說明符用在模板中的任何地方,與內建型別說明符或類型別說明符的使用方式完全相同,即可以用於指定返回型別,變數宣告等。

    作者原版:1.2、 不能為同一個模板型別形參指定兩種不同的型別,比如template<class T>void h(T a, T b){},語句呼叫h(2, 3.2)將出錯,因為該語句給同一模板形參T指定了兩種型別,第一個實參2把模板形參T指定為int,而第二個實參3.2把模板形參指定為double,兩種型別的形參不一致,會出錯。(針對函式模板)

    作者原版:1.2針對函式模板是正確的,但是忽略了類模板。下面將對類模板的情況進行補充。

    本人新增1.2補充版(針對於類模板)、當我們宣告類物件為:A<int> a,比如template<class T>T g(T a, T b){},語句呼叫a.g(2, 3.2)在編譯時不會出錯,但會有警告,因為在宣告類物件的時候已經將T轉換為int型別,而第二個實參3.2把模板形參指定為double,在執行時,會對3.2進行強制型別轉換為3。當我們宣告類的物件為:A<double> a,此時就不會有上述的警告,因為從intdouble是自動型別轉換。

演示示例1:

  TemplateDemo.h

複製程式碼
 1 #ifndef TEMPLATE_DEMO_HXX
 2 #define TEMPLATE_DEMO_HXX
 3 
 4 template<class T> class A{
 5     public:
 6         T g(T a,T b);
 7         A();
 8 };
 9 
10 #endif
複製程式碼

  TemplateDemo.cpp

複製程式碼
 1 #include<iostream.h>
 2 #include "TemplateDemo.h"
 3 
 4 template<class T> A<T>::A(){}
 5 
 6 template<class T> T A<T>::g(T a,T b){
 7     return a+b;
 8 }
 9 
10 void main(){
11     A<int> a;
12     cout<<a.g(2,3.2)<<endl;
13 }
複製程式碼

  編譯結果:

複製程式碼
1 --------------------Configuration: TemplateDemo - Win32 Debug--------------------
2 Compiling...
3 TemplateDemo.cpp
4 G:\C++\CDaima\TemplateDemo\TemplateDemo.cpp(12) : warning C4244: 'argument' : conversion from 'const double' to 'int', possible loss of data
5 
6 TemplateDemo.obj - 0 error(s), 1 warning(s)
複製程式碼

  執行結果:      5  

  我們從上面的測試示例中可以看出,並非作者原作中的那麼嚴密!此處僅是本人跟人測試結果!請大家本著實事求是的態度,自行驗證!

  2、非型別形參

    2.1 、非型別模板形參:模板的非型別形參也就是內建型別形參,如template<class T, int a> class B{};其中int a就是非型別的模板形參。

    2.2、 非型別形參在模板定義的內部是常量值,也就是說非型別形參在模板的內部是常量。

    2.3、 非型別模板的形參只能是整型,指標和引用,像double,String, String **這樣的型別是不允許的。但是double &,double *,物件的引用或指標是正確的。

    2.4、 呼叫非型別模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結果。

    2.5 、注意:任何區域性物件,區域性變數,區域性物件的地址,區域性變數的地址都不是一個常量表達式,都不能用作非型別模板形參的實參。全域性指標型別,全域性變數,全域性物件也不是一個常量表達式,不能用作非型別模板形參的實參。

    2.6、 全域性變數的地址或引用,全域性物件的地址或引用const型別變數是常量表達式,可以用作非型別模板形參的實參

    2.7 、sizeof表示式的結果是一個常量表達式,也能用作非型別模板形參的實參。

    2.8 、當模板的形參是整型時呼叫該模板時的實參必須是整型的,且在編譯期間是常量,比如template <class T, int a> class A{};如果有int b,這時A<int, b> m;將出錯,因為b不是常量,如果const int b,這時A<int, b> m;就是正確的,因為這時b是常量。

    2.9 、非型別形參一般不應用於函式模板中,比如有函式模板template<class T, int a> void h(T b){},若使用h(2)呼叫會出現無法為非型別形參a推演出引數的錯誤,對這種模板函式可以用顯示模板實參來解決,如用h<int, 3>(2)這樣就把非型別形參a設定為整數3。顯示模板實參在後面介紹。

    2.10、 非型別模板形參的形參和實參間所允許的轉換
      1、允許從陣列到指標,從函式到指標的轉換。如:template <int *a> class A{}; int b[1]; A<b> m;即陣列到指標的轉換
      2、const修飾符的轉換。如:template<const int *a> class A{}; int b; A<&b> m;   即從int *到const int *的轉換。
      3、提升轉換。如:template<int a> class A{}; const short b=2; A<b> m; 即從short到int 的提升轉換
      4、整值轉換。如:template<unsigned int a> class A{};   A<3> m; 即從int 到unsigned int的轉換。
      5、常規轉換。

非型別形參演示示例1:

  由使用者自己親自指定棧的大小,並實現棧的相關操作。

  TemplateDemo.h

複製程式碼
 1 #ifndef TEMPLATE_DEMO_HXX
 2 #define TEMPLATE_DEMO_HXX
 3 
 4 template<class T,int MAXSIZE> class Stack{//MAXSIZE由使用者建立物件時自行設定
 5     private:
 6         T elems[MAXSIZE];    // 包含元素的陣列
 7         int numElems;    // 元素的當前總個數
 8     public:
 9         Stack();    //建構函式
10         void push(T const&);    //壓入元素
11         void pop();        //彈出元素
12         T top() const;    //返回棧頂元素
13         bool empty() const{     // 返回棧是否為空
14             return numElems == 0;
15         }
16         bool full() const{    // 返回棧是否已滿
17             return numElems == MAXSIZE;
18         }
19 };
20 
21 template <class T,int MAXSIZE> 
22 Stack<T,MAXSIZE>::Stack():numElems(0){     // 初始時棧不含元素
23     // 不做任何事情
24 }
25 
26 template <class T,int MAXSIZE>
27 void Stack<T, MAXSIZE>::push(T const& elem){
28     if(numElems == MAXSIZE){
29         throw std::out_of_range("Stack<>::push(): stack is full");
30     }
31     elems[numElems] = elem;   // 附加元素
32     ++numElems;               // 增加元素的個數
33 }
34 
35 template<class T,int MAXSIZE>
36 void Stack<T,MAXSIZE>::pop(){
37     if (numElems <= 0) {
38         throw std::out_of_range("Stack<>::pop(): empty stack");
39     }
40     --numElems;               // 減少元素的個數
41 }
42 
43 template <class T,int MAXSIZE>
44 T Stack<T,MAXSIZE>::top()const{
45     if (numElems <= 0) {
46         throw std::out_of_range("Stack<>::top(): empty stack");
47     }
48     return elems[numElems-1];  // 返回最後一個元素
49 }
50 
51 #endif
複製程式碼

  TemplateDemo.cpp

複製程式碼
 1 #include<iostream.h>
 2 #include <iostream>
 3 #include <string>
 4 #include <cstdlib>
 5 #include "TemplateDemo.h"
 6 
 7 int main(){
 8     try {
 9         Stack<int,20>  int20Stack;  // 可以儲存20個int元素的棧
10         Stack<int,40>  int40Stack;  // 可以儲存40個int元素的棧
11         Stack<std::string,40> stringStack; // 可儲存40個string元素的棧
12 
13         // 使用可儲存20個int元素的棧
14         int20Stack.push(7);
15         std::cout << int20Stack.top() << std::endl;    //7
16         int20Stack.pop();
17 
18         // 使用可儲存40個string的棧
19         stringStack.push("hello");
20         std::cout << stringStack.top() << std::endl;    //hello
21         stringStack.pop();    
22         stringStack.pop();    //Exception: Stack<>::pop<>: empty stack
23         return 0;
24     }
25     catch (std::exception const& ex) {
26         std::cerr << "Exception: " << ex.what() << std::endl;
27         return EXIT_FAILURE;  // 退出程式且有ERROR標記
28     }
29 }
複製程式碼

  執行結果:

    

 非型別形參演示示例2:

  TemplateDemo01.h

複製程式碼
 1 #ifndef TEMPLATE_DEMO_O1
 2 #define TEMPLATE_DEMO_01
 3 
 4 template<typename T> class CompareDemo{
 5     public:
 6         int compare(const T&, const T&);
 7 };
 8 
 9 template<typename T> 
10 int CompareDemo<T>::compare(const T& a,const T& b){
11     if((a-b)>0)
12         return 1;
13     else if((a-b)<0)
14         return -1;
15     else
16         return 0;
17 }
18 
19 #endif
複製程式碼

  TemplateDemo01.cpp

複製程式碼
1 #include<iostream.h>
2 #include "TemplateDemo01.h"
3 
4 void main(){
5     CompareDemo<int> cd;
6     cout<<cd.compare(2,3)<<endl;
7 }
複製程式碼

相關推薦

c++ 模板1

C++模板 本文轉自:http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html   模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的

C++ 模板

創建 規則 error ++ 例如 public err iostream () 四、類模板的默認模板類型形參   1、可以為類模板的類型形參提供默認值,但不能為函數模板的類型形參提供默認值。函數模板和類模板都可以為模板的非類型形參提供默認值。   2、類模板的類型形

Extreme Drift賽車遊戲C#原始碼1

Extreme Drift賽車遊戲C#原始碼詳解(1) C#我只是一個萌新,由於搞過Java,還是可以看懂C#的 偶然間得到賽車遊戲Extreme Drift的原始碼 接下來我會花一段時間來解讀,這是一個我學習的過程,記錄在部落格 等到我完全解讀之後,我也許會考慮再加入聯機功能等

C++ 模板

C++模板   模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的引數、返回值取得任意型別。   模板是一種對型別進行引數化的工具;   通常有兩種形式:函式模板和類模板;   函式模板針對僅引

C++模板

轉載自:http://www.cnblogs.com/gw811/archive/2012/10/25/2736224.html C++模板 四、類模板的預設模板型別形參   1、可以為類模板的型別形參提供預設值,但不能為函式模板的型別形參提供預設值。函式模板

c++模板

轉載自http://www.cnblogs.com/gw811/archive/2012/10/25/2738929.html C++模板   模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的引數、

C語言1資料型別

資料型別 關於C語言詳解系列部落格的目錄:https://blog.csdn.net/snake_lp/article/details/78630717點選開啟連結一,概述資料型別就是固定記憶體大小空間

C++之模板

C++模板   模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的引數、返回值取得任意型別。   模板是一種對型別進行引數化的工具;   通常有兩種形式:函式模板和類模板;   函式模板針對僅引數型別不同的函式;   類模板針對僅資料成

C++之類模板

C++ 除了支援函式模板,還支援類模板(Class Template)。函式模板中定義的型別引數可以用在函式宣告和函式定義中,類模板中定義的型別引數可以用在類宣告和類實現中。類模板的目的同樣是將資料的型別引數化。 宣告類模板的語法為: template<typenam

C語言簡單說】二:第一個C語言程式1

如有錯誤請給與糾正… 上一個教程只說明瞭第一個C語言程式原始碼中的: printf("Hello Wrold!"); 這行程式碼的含義,現在我們來說說全部程式碼;當然為了各位的方便,我就把那個程式

MongoDB執行計劃分析1

mongo smu pre als comm 計劃 -- {} direct 正文 queryPlanner queryPlanner是現版本explain的默認模式,queryPlanner模式下並不會去真正進行query語句查詢,而是針對query語句進行執行計劃分析並

跟開濤學SpringMVC4.1:Controller接口控制器1

詳解 shu fix gmv 控制器 input abstract pre pdf http://www.importnew.com/19397.html http://blog.csdn.net/u014607184/article/details/5207453

指標1-- 軌道線指標ENE

本質 平均值 width 簡單 公式 方向 重新 alt 改變 軌道線指標(ENE): 1、定義:軌道線(ENE)由上軌線(UPPER)和下軌線(LOWER)及中軌線(ENE)組成,軌道線的優勢在於其不僅具有趨勢軌道的研判分析作用,也可以敏銳的覺察股價運行過程中方向的改

JAVA線程池原理1

err 最大 RKE private queue 分享 ren ++ ant 線程池的優點 1、線程是稀缺資源,使用線程池可以減少創建和銷毀線程的次數,每個工作線程都可以重復使用。 2、可以根據系統的承受能力,調整線程池中工作線程的數量,防止因為消耗過多內存導致服務器崩潰。

Spring Boot中使用MyBatis註解配置1

sql type .org 實體 sch 整合 PE 匯總 同傳 之前在Spring Boot中整合MyBatis時,采用了註解的配置方式,相信很多人還是比較喜歡這種優雅的方式的,也收到不少讀者朋友的反饋和問題,主要集中於針對各種場景下註解如何使用,下面就對幾種常見的情況舉

Spring Cloud Spring Boot mybatis分布式微服務雲架構屬性配置文件1

定義 public 配置數據庫連接 clas cep and xml配置 其他 PE 相信很多人選擇Spring Boot主要是考慮到它既能兼顧Spring的強大功能,還能實現快速開發的便捷。我們在Spring Boot使用過程中,最直觀的感受就是沒有了原來自己整合Spri

基於Tomcat的JSP 1—— 概述

normal pad san borde orm ace text pin style 們使用。 一.為什麽使用JSP 下面基於Tomcat的JSP 詳解(1)—— 概述

Java1--知識點總結1

大資料NP知識點總結1 ---------------------  作者:文動天下 來源:CSDN  連結:https://blog.csdn.net/li_yi_kun?t=1 版權宣告:本文為博主原創文章,轉載請附上博文連結! 目錄 上午

pg資料庫1--Mac安裝postgreSQL

Mac安裝postgreSQL詳解 ---------------------  作者:文動天下 來源:CSDN  連結:https://blog.csdn.net/li_yi_kun?t=1 版權宣告:本文為博主原創文章,轉載請附上博文連結! 建議用

C# 特性Attribute

今天整理關於特性的資料,之前都忘了,今天整理一下 參考《C#高階程式設計》第10版 0X01 特性(Attribute) 特性定義 特性不會影響編譯過程,因為編譯器不能識別它們,但這些特性在應用於程式元素