程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 模板的簡單介紹與使用,模板簡單介紹使用

模板的簡單介紹與使用,模板簡單介紹使用

編輯:C++入門知識

模板的簡單介紹與使用,模板簡單介紹使用


什麼是模板?


模板(template)指c++中的函數模板與類模板,大體對應於C#和Java眾的泛型的概念。目前,模板已經成為C++的泛型編程中不可缺少的一部分。

模板定義以關鍵字template開始,後接模板形參表,模板形參表是用尖括號括住的一個或者多個模板形參的列表,形參之間以逗號分隔。 模板形參可以是表示類型的類型形參,也可以是表示常量表達式的非類型形參。非類型形參跟在類型說明符之後聲明。類型形參跟在關鍵字class或typename之後定義(至於class與typename的區別實際並不大,c++的早期版本中只有class,沒有typename。在絕大多數場景下兩者是通用的,只有少數特殊情況下必須使用typename。總之,使用typename是萬無一失的。兩者的區別可以參考這篇文章)。

模板是C++程序員絕佳的武器, 特別是結合了多重繼承(multiple inheritance)與運算符重載(operator overloading)之後。C++ 的標准庫提供許多有用的函數大多結合了模板的觀念,如STL以及IO Stream。

 

 函數模板

  所謂函數模板,實際上是建立一個通用函數,其函數類型和形參類型不具體指定,用一個虛擬的類型來代表。這個通用函數就稱為函數模板。凡是函數體相同的函數都可以用這個模板來代替,不必定義多個函數,只需在模板中定義一次即可。在調用函數時系統會根據實參的類型來取代模板中的虛擬類型,從而實現了不同函數的功能。 

  網上大多數介紹都是從比較兩個數大小入手的,本文章介紹依然如此,假設有一個需要要比較兩個數的大小,但是這兩個數的類型是不確定的,可能是int、float、double類型的。

  當然有一種方式就是可以用函數的重載來實現,但用重載的方式造成的問題是:有多少類型的可能性,就要寫多少個重載函數。假設當前需求裡可能要求只有float和double兩種類型,但有一天增加了對int類型的允許,則要在代碼中增加對int類型參數的重載函數。

  這個時候,函數模板就排上用場了。只需要定義一個帶有泛型參數的函數,就可以實現多種類型參數的比較,直接看下面的代碼吧:

  

復制代碼
 1 class MyTemplate
 2 {
 3 public:
 4     MyTemplate(void);
 5     ~MyTemplate(void);
 6 
 7     //以關鍵字template開頭 後面接<typename T>或<class T> 返回值類型和參數類型都是T
 8     template <typename T> T Max(T a,T b) 
 9     {
10         return a>=b?a:b;
11     };
12 };
復制代碼

這裡的T會在程序編譯的時候特化為代碼調用處傳入的實際參數,例如

如果比較兩個int類型的大小:

    MyTemplate mytemplate;
    int x = 10;
    int y = 100;
    int val = mytemplate.Max(x,y);    

程序裡的T在編譯時就用int替換,替換後的程序應該是下面這個樣子:

  template <typename int> int Max(int a,int b) 
  {
      return a>=b?a:b;
  };

float和double類型的是同樣的道理,這裡就不重復了。

類模板

 當我們有更加復雜的需求的時候,例如要實現一個隊列,這個隊列中可能不止有int類型的數據,還有可能有string類型、double類型、或者更復雜的自定義類型。例如下面這個隊列中存儲了多種數據類型的對象,這個時候就需要定義一個類模板了。

類模板實現的簡單隊列


1 #pragma once 2 3 template <typename T> class FZQueue; 4 template <typename T> class queueItem 5 { 6 private: 7 friend class FZQueue<T>; //因為queueItem只有FZQueue要直接調用 所以這裡要聲明為FZQueue的友元 8 9 queueItem(void){}; 10 11 queueItem(const T &item1,const queueItem *p):item(item1),next(p){}; 12 13 queueItem(const T &t):item(t),next(0){}; //顯式定義復制構造函數 將item置為t next設置為空指針 14 15 ~queueItem(void){};//析構函數 16 17 T item; 18 19 queueItem *next; 20 }; 21 22 template <typename T> 23 class FZQueue 24 { 25 public: 26 FZQueue(void):head(0),tail(0){}; 27 ~FZQueue(void) 28 { 29 destroy(); 30 }; 31 32 /*顯式定義復制構造函數 可以不顯式的聲明 對此功能無影響*/ 33 FZQueue(const FZQueue &t):head(0),tail(0) // 將t中的每個元素的值拷貝到新聲明的對象中 必須對指針head和tail初始化 否則調用時會報內存訪問異常 34 { 35 copy_elements(t); 36 }; 37 38 FZQueue& operator=(const FZQueue&); //顯式賦值操作符重載 可以不顯式的聲明 對此功能無影響 39 40 T& front() //獲取隊列頭 41 { 42 return head->item; 43 }; 44 45 46 void push(const T &item) //添加元素到隊尾 47 { 48 queueItem<T> *p_item = new queueItem<T>(item); 49 if(empty()) 50 { 51 head=tail=p_item; 52 } 53 else 54 { 55 tail->next = p_item; 56 tail=p_item; 57 } 58 }; 59 60 void pop() //刪除隊列頭元素 61 { 62 queueItem<T> *p_curHead = head; 63 head=head->next; 64 delete p_curHead; 65 }; 66 67 bool empty() //判斷隊列是否為空 68 { 69 return head==0; 70 }; 71 72 private: 73 queueItem<T> *head; //隊列頭元素的指針 74 75 queueItem<T> *tail; //隊尾元素的指針 76 77 void destroy() 78 { 79 while (!empty()) 80 { 81 pop(); 82 } 83 }; 84 85 void copy_elements(const FZQueue &t) 86 { 87 queueItem<T> *p = t.head; 88 //int i = 0; 89 while (p) /*i<5*/ 90 { 91 push(p->item); 92 p = p->next; 93 //i++; 94 } 95 }; 96 97 }; View Code

 調用代碼如下:

1 if(valIndexs.empty()) 2 { 3 for (int zi = 10;zi!=15;zi++) 4 { 5 valIndexs.push(zi); 6 } 7 } 8 FZQueue<int> clone_valZindexs(valIndexs); 9 10 cout<<"valIndexs:"<<valIndexs.front()<<"______clone_valZindexs:"<<clone_valZindexs.front()<<endl; 11 12 cout<<"valIndexs:"<<valIndexs.front()<<"______clone_valZindexs:"<<clone_valZindexs.front()<<endl; View Code

 以上就是用類模板實現簡單隊列的完整代碼。

問題與總結


1.把類中的構造函數重載(FZQueue(const T &t);)和操作符重載(FZQueue& operator=(const FZQueue&);)去掉後都是一樣正常執行,不知道這個構造函數重載和操作符重載在什麼情況下使用。

總結:參考《C++ Primer》第四版第13章 復制控制 裡介紹的復制構造函數一節,對復制構造函數的描述是這樣的:

復制代碼
復制構造函數是一種特殊構造函數,具有單個形參,該形參(常用const修飾)是對該類類型的引用。當定義一個新對象並用一個同類型的對象對它進行初始化時,將顯式使用復制構造函數。當將該類型的對象傳遞給函數或從函數返回該類型的對象時,將隱式使用復制構造函數。可用於:

1.根據另一個同類型的對象顯示或隱式初始化一個對象
2.復制一個對象,將它的作為實參傳遞給一個函數
3.從函數返回時復制一個對象
4.初始化順序容器中的元素
5.根據元素初始化式列表初始化數組元素
復制代碼

並且:如果程序中沒有顯示定義並實現復制構造函數,編譯器會自動生成。賦值操作符重載與析構函數都是如此。

    不能將自定義的類聲明為指針形式,例如FZQueue<int> *clone_zindexs,如果這樣做,之後將這個指針當參數調用復制構造函數時,復制構造函數不起作用,因為這裡只是聲明了一個指針而已。

 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved