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

C++模板詳解(一),模板詳解

編輯:C++入門知識

C++模板詳解(一),模板詳解


參考:C++ 模板詳解(一)

 

模板:對類型進行參數化的工具;通常有兩種形式: 

  • 函數模板:僅參數類型不同;
  • 類模板:   僅數據成員成員函數類型不同。

目的:讓程序員編寫與類型無關的代碼。

注意:模板的聲明或定義只能在全局、命名空間、類范圍內進行。即不能在局部范圍、函數內進行,比如不能在main函數中聲明或定義一個模板。

 

一 函數模板

1 函數模板的格式: 

template <class 類型名1,class 類型名2,......> 
返回類型 函數名(參數列表)
{
    函數體
}
  • template、class是關鍵字,class可以用關鍵字typename代替,在這裡typename和class沒區別
  • <>中的參數叫模板形參不能為空。模板形參用傳入的實參類型來初始化。
  • 模板形參可以在 返回類型、參數列表、函數體內使用。一旦編譯器確定了模板實參類型,就稱他實例化了函數模板的一個實例。

 

例1:swap函數的模板:

template <class T> 
void swap(T& a, T& b)   //參數列表使用模板形參
{
T tmp = a; //函數體內使用模板形參
a = b;
b = tmp; }
  • 當調用這個函數模板時,類型T就會被被調用時的類型所代替
  • 比如swap(a,b),其中a和b是int 型,模板函數就變為swap(int &a, int &b)。
  • 而當swap(c,d),其中c和d是double型時,模板函數會被替換為swap(double &a, double &b)。

 

例2:max函數的模板 

 1 #include<iostream>
 2 
 3 template<typename T>
 4 const T& myMax(const T &a, const T &b)
 5 {
 6   return a > b ? a : b;
 7 }
 8  
 9 int main()
10 {
11   cout << myMax(2.1, 2.2) << endl;         //輸出2.2  模板實參被隱式推演成double
12   cout << myMax<double>(2.1, 2.2) << endl; //輸出2.2  顯示指定模板參數。
13   cout << myMax<int>(2.1, 2.2) << endl;    //輸出2    顯示指定的模板參數,會將參數轉換為int。
14 
15   return 0; 16 }

  

2、注意:

  • 不存在swap(int, int)這樣的調用! 即不能用類型初始化,只能用實參推演來進行。即根據2來推出int型。
  • 即只能進行swap(2, 3);  或者  int a, b; swap(a,b);   這樣的調用。

 

二 類模板

1 類模板的格式為:

template<class 形參名1, class 形參名2, ...>
class 類名
{
    ... 
};
  • 與函數模板一樣,以template開始,後接模板形參列表模板形參不能為空
  • 類的數據成員和函數成員可以使用模板形參。

 

:一個類模板的例子:

template<class T> 
class A
{
public: 
    T a;               //類成員使用模板形參
    T b; 
    T func(T c, T &d);
};

 

2 類模板對象的創建:

  • 方法:A<int> m;  類A中用到模板形參的地方都會被int 所代替。
  • 兩個模板形參:A<int, double> m;  類型之間用逗號隔開。

3 類模板形參必須指定類型而不是實參:

  • 必須這樣指定 A<int> m;  明確指定類型。
  • 不能這樣使用A<2> m;  類模板形參不存在實參推演的問題。

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

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

 

:比如模板類A,有兩個模板形參T1,T2,有一個成員函數 void func(),則外部定義該函數的語法為:

template<class T1, class T2>   //與類一致
void A<T1, T2>::func()      //類名也要加上模板參數列表
{
}

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

 

三 模板的形參

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

1 類型形參 

類型形參由關鍵字class或typename後接說明符構成,如

template<class T> 
void func(T a)
{
};
  • 其中 就是一個類型形參,名字由用戶確定。

 

函數模板,同一個類型形參,必須用相同類型的實參來初始化,比如

template<class T>
void func(T a, T b)
{
}
  • 調用 func(2, 3.2); 將編譯出錯,因為模板形參T同時被指定為int 和 double,不一致,會出錯。

 

類模板,其內部成員函數,則沒有上述的限制,比如 

template<class T>
class A
{
public:
    T func(T a, T b);   //或者T func(const T &a, const T &b);  普通引用會編譯報錯
};
  • 聲明 A<int> a;  調用 a.func(2, 3.2);  在編譯時不會出錯
  • 第二個實參3.2類型為double,在運行時,會強制類型轉換為3。

 

:模板類的對象調用成員函數: 

 1 #include <iostream>
 2 using namespace std;
 3 
 4 template<class T>
 5 class A
 6 {
 7 public:
 8     A();
 9     T func(T a, T b);
10 };
11 
12 template<class T>     //類外定義構造函數
13 A<T>::A()
14 {
15 }
16 
17 template<class T>     //類外定義成員函數
18 T A<T>::func(T a, T b)
19 {
20     return a + b;
21 }
22 
23 int main(int argc, char *argv[])
24 {
25     A<int> ia;                        //模板實參為int類型的對象
26     cout << ia.func(3, 2.1) << endl;  //輸出5
27 
28     A<double> da;
29     cout << da.func(3, 2.1) << endl;  //輸出5.1
30 
31     return 0;
32 }

  

2 非類型形參

也就是內置類型形參,如

template<class T, int a>   //int a 就是非類型形參
class B
{
};

非類型形參有幾點要注意的:

  • 在模板定義的內部是常量值,也就是說,上面的a在類B內部是一個常量。
  • 形參只能是整型、指針、引用,像double、string、string **是不允許的,但是double &、double *、對象的引用或指針是正確的。
  • 實參必須是一個常量表達式,即必須能在編譯時計算出結果。注意:局部對象/變量和其地址,全局指針/變量/對象,都不是常量表達式;全局變量/對象地址或引用const類型變量sizeof的結果,都是常量表達式。
  • 形參是整型時,實參也必須是整型的,且在編譯期間是常量,比如
template <class T, int a> 
class A
{
};

如果有int b;  這時 A<int, b> m; 將出錯,因為b不是常量;如果有 const int b;  這時 A<int, b> m;  正確,因為這時b是常量。

  • 非類型形參一般不應用於函數模板中,比如有函數模板
template<class T, int a> 
void func(T b)
{
}

若用func(2)調用,會出現無法為非類型形參a推演出參數的錯誤;可以用顯示模板實參來解決,如用func<int, 3>(2); 把非類型形參a設置為整數3。顯示模板實參在後面介紹。

  • 形參實參間所允許的轉換: 
//1 數組到指針,函數到指針的轉換
template<int *a>
class A { };
int b[10];
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 #include <iostream> 
  2 #include <string> 
  3 #include <stdexcept>  //std::out_of_range
  4 #include <cstdlib>    //EXIT_FAILURE
  5 using namespace std;
  6 
  7 /*********模板類,聲明開始,一般都是放在頭文件的*********/
  8 
  9 template<class T, int MAXSIZE>
 10 class myStack
 11 {
 12 public:
 13     myStack();
 14     void push(T const &);  //入棧
 15     void pop();            //出棧
 16     T    top() const;      //返回棧頂
 17 
 18     bool empty() const     //判斷是否為空
 19     {
 20         return size == 0;
 21     }
 22 
 23     bool full() const      //判斷棧是否已滿
 24     {
 25         return size == MAXSIZE;
 26     }
 27 
 28 private:
 29     T   elems[MAXSIZE];    //使用數組存放棧元素,由於非類型形參MAXSIZE在類內是一個常量,所以可以用來聲明數組大小
 30     int size;              //棧已使用空間
 31 };
 32 
 33 /**********模板類,聲明結束,定義成員函數開始********/
 34 
 35 template<class T, int MAXSIZE>
 36 myStack<T, MAXSIZE>::myStack(): size(0)      //構造函數,初始化為空
 37 {
 38 }
 39 
 40 template<class T, int MAXSIZE>
 41 void myStack<T, MAXSIZE>::push(T const &new_elem)     //入棧
 42 {
 43     if(size == MAXSIZE)
 44     {
 45         throw out_of_range("myStack::push(): stack is full");
 46     }
 47     elems[size++] = new_elem;
 48 }
 49 
 50 template<class T, int MAXSIZE>
 51 void myStack<T, MAXSIZE>::pop()       //棧頂出棧
 52 {
 53     if(size <= 0)
 54     {
 55         throw out_of_range("myStack::pop(): stack is empty");
 56     }
 57     --size;
 58 }
 59 
 60 template<class T, int MAXSIZE>
 61 T myStack<T, MAXSIZE>::top() const    //返回棧頂元素
 62 {
 63     if(size <= 0)
 64     {
 65         throw out_of_range("myStack::top(): stack is empty");
 66     }
 67     return elems[size - 1];
 68 }
 69 
 70 /***********成員函數定義結束**********************/
 71 
 72 int main(int argc, char *argv[])
 73 {
 74     try
 75     {
 76         myStack<int,    20>  int20Stack;
 77         myStack<int,    40>  int40Stack;
 78         myStack<string, 40>  stringStack;
 79 
 80         int20Stack.push(7);
 81         cout << int20Stack.top() << endl;   //輸出7
 82         int20Stack.pop();
 83 
 84         for(int i = 0; i < 40; ++i)
 85             int40Stack.push(i);
 86         cout << int40Stack.top() << endl;   //輸出39
 87         //int40Stack.push(41);              //繼續添加元素,會拋出異常,輸出Exception: myStack::push(): stack is full
 88         
 89         stringStack.push("hello");
 90         cout << stringStack.top() << endl;  //輸出hello
 91         stringStack.pop();
 92         //stringStack.pop();                //繼續出棧,會拋出異常,輸出Exception: myStack::push(): stack is empty
 93 
 94         return 0;
 95     }
 96     catch(out_of_range const &ex)
 97     {
 98         cerr << "Exception: " << ex.what() << endl;
 99         return EXIT_FAILURE;
100     }
101 }

  


C\C++模板、類定義問題,詳解,


一道c++模板題目,詳解

T是類型參數,
T也是模板形參(有兩種模板形參:類型形參和非類型形參),
T還是模板形參表(你表項只有一項).
 

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