1. 程式人生 > 實用技巧 >C++——模板小結

C++——模板小結

模板

模板(Template)指C++程式設計設計語言中採用型別作為引數的程式設計,支援通用程式設計。C++ 的標準庫提供許多有用的函式大多結合了模板的觀念,如STL以及IO Stream。模板是C++支援引數化多型的工具,使用模板可以使使用者為類或者函式宣告一種一般模式,使得類中的某些資料成員或者成員函式的引數、返回值取得任意型別。

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

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

(一)函式模板

1 template <class 形參名,class 形參名,......> 
2  返回型別 函式名(引數列表)
3 
4    {
5 
6       函式體
7 8    }

下面我們以一個交換變數的程式來展示函式模板的作用

 1 #include<iostream>
 2 using namespace std;
 3 void Swap(int &a,int &b)
 4 {
 5     int t = a;
 6     a = b;
 7     b = t;
 8 }
 9 int main(int argc, char const *argv[])
10 {
11     int a = 23,b = 45;
12     char c = 'a',d = 'b';
13     Swap(a,b);
14 //Swap(c,d); //報錯,因為我們只能交換int型別的兩個變數 15 cout<<a<<" "<<b<<endl; 16 return 0; 17 }

這是我們普通的交換變數的程式,但它有侷限,我們只能交換一種我們規定好的型別變數,換一種就不能交換了,當然我們也可以用過載的辦法,但過載就意味著我們要寫出包括所有情況的函式,這樣工作量很大,有沒有簡單一點的辦法呢?這時候我們就要用到模板了,用一個函式來實現所有的。

 1 #include<iostream>
 2 using namespace std;
3 void Swap(int &a,int &b) 4 { 5 int t = a; 6 a = b; 7 b = t; 8 } 9 template<class T> 10 void Swap(T &a,T &b) 11 { 12 T temp = a; 13 a = b; 14 b = temp; 15 } 16 int main(int argc, char const *argv[]) 17 { 18 int a = 23,b = 45; 19 char c = 'a',d = 'b'; 20 Swap(a,b); 21 cout<<a<<" "<<b<<endl; 22 Swap(c,d); 23 cout<<c<<" "<<d<<endl; 24 return 0; 25 }

看結果,我們可以看到,不僅是int型的,char型的也可以交換,當然我們還可以交換double型的,但大家注意,因為我們前面有寫普通的交換函式,所有會預設呼叫普通的函式,原因是雖然編譯器會幫我們做型別轉換(T變成int),但能不轉的時候編譯器不會轉,大家可以試試,那如果我們要指定用我們寫的模板函式怎麼辦呢?

1 Swap<>(int a,int b)//這樣就會指定呼叫模板函式
 1 #include<iostream>
 2 using namespace std;
 3 void Swap(int &a,int &b)
 4 {
 5     cout<<"普通函式"<<endl;
 6     int t = a;
 7     a = b;
 8     b = t;
 9 }
10 template<class T>
11 void Swap(T &a,T &b)
12 {
13     cout<<"模板函式"<<endl;
14     T temp = a;
15     a = b;
16     b = temp;
17 }
18 int main(int argc, char const *argv[])
19 {
20     int a = 23,b = 45;
21     char c = 'a',d = 'b';
22     Swap(a,b);
23     cout<<a<<" "<<b<<endl;
24     Swap(c,d);
25     cout<<c<<" "<<d<<endl;
26     return 0;
27 }

看上面的執行結果我們可以發現,編譯器呼叫的是普通函式,當我們指定要呼叫模板函式時

 1 #include<iostream>
 2 using namespace std;
 3 void Swap(int &a,int &b)
 4 {
 5     cout<<"普通函式"<<endl;
 6     int t = a;
 7     a = b;
 8     b = t;
 9 }
10 template<class T>
11 void Swap(T &a,T &b)
12 {
13     cout<<"模板函式"<<endl;
14     T temp = a;
15     a = b;
16     b = temp;
17 }
18 int main(int argc, char const *argv[])
19 {
20     int a = 23,b = 45;
21     char c = 'a',d = 'b';
22     Swap<>(a,b);
23     cout<<a<<" "<<b<<endl;
24     Swap(c,d);
25     cout<<c<<" "<<d<<endl;
26     return 0;
27 }

我們可以看到,在Swap後面加<>之後就會指定呼叫模板函式。或者Swap<int>(a,b),這樣編譯器就不用去進行型別推導了。

(二)類模板

模板類格式如下:

1 template<class  形參名,class 形參名,…>
2     class 類名{ ... };

類模板的話,我們以一個例項來引出:

需求:
寫一個各邊長度的陣列類Array
用於存放各種元素,個數未知
設計:
內部動態申請一個buffer
capacity:表示buffer的大小
size:表示buffer已經存放元素的個數
介面:
Size():
Capacity():最大容量
Clear():清空,使size為0,capacity不變
PushBack():新增一個元素。

問題:這個問題我們首先以double型的為例,程式碼如下:

 1 #include<iostream>
 2 using namespace std;
 3 #include<string.h>
 4 class Myarry
 5 {
 6 public:
 7     Myarry(int capacity = 5)    //初始的陣列記憶體
 8     {
 9         m_buffer = new double[capacity];  //開闢一個大小為5的記憶體空間
10         m_size = 0;                      //初始size為0
11         m_capacity = capacity;           //初始的大小為5
12     }
13     int Size()                    //已存元素的個數
14     {
15         return m_size;
16     }
17     int Capacity()               //開闢的記憶體大小
18     {
19         return m_capacity;
20     }
21     void Clear()                  //清空陣列
22     {
23         delete []m_buffer;        //刪除陣列
24         m_size = 0;               //元素個數清0
25         cout<<"Arrary cleared!"<<endl;
26     }
27     void PushBack(double val)     //新增元素
28     {
29         if(m_size >= m_capacity)  //如果陣列已存的元素個數大於開闢的空間,就開闢記憶體
30         {
31             cout<<"Expand the Memory!"<<endl;
32             m_capacity = m_capacity + 2;   //擴大兩個元素的大小
33             double *buffer = new double[m_capacity];  //開闢一個新的空間
34             memcpy(buffer,m_buffer,m_capacity * sizeof(double));  //把舊的陣列的元素拷貝一下
35             delete []m_buffer;   //刪除舊的陣列
36             m_buffer = buffer;    //讓陣列指標指向新陣列
37         }
38         m_buffer[m_size++] = val;   //把val的值新增到陣列中,陣列已存長度+1
39     }
40 private:
41     double *m_buffer;             //陣列指標
42     int m_capacity;              //陣列記憶體大小
43     int m_size;                  //陣列已存元素個數
44 };
45 int main(int argc, char const *argv[])
46 {
47     Myarry a;                   //例項化出一個物件
48     cout<<"Capacity:"<<a.Capacity()<<endl;  //輸出初始陣列的大小和已存元素個數
49     cout<<"Size:"<<a.Size()<<endl;
50     a.PushBack(2.3);     //新增五個元素
51     a.PushBack(1.1);
52     a.PushBack(3.4);
53     a.PushBack(2.1);
54     a.PushBack(3.7);
55     cout<<"Capacity:"<<a.Capacity()<<endl;  //輸出現在陣列的大小和已存元素個數
56     cout<<"Size:"<<a.Size()<<endl;
57     a.PushBack(4.5);              //再新增一個元素,此時已經大於陣列的記憶體大小,需要擴增記憶體
58     cout<<"Capacity:"<<a.Capacity()<<endl;  //輸出現在陣列的大小和已存元素個數
59     cout<<"Size:"<<a.Size()<<endl;
60     a.Clear();    //清空陣列
61     cout<<"Capacity:"<<a.Capacity()<<endl;  //輸出現在陣列的大小和已存元素個數
62     cout<<"Size:"<<a.Size()<<endl;
63     return 0;
64 }

上面是我們用普通的類實現,但需求是要實現各種型別的,那我們怎麼解決呢?這時候就要用到模板類了。

 1 #include<iostream>
 2 using namespace std;
 3 #include<string.h>
 4 template <class T>
 5 class Myarry
 6 {
 7 public:
 8     Myarry(int capacity = 5)    //初始的陣列記憶體
 9     {
10         m_buffer = new T[capacity];  //開闢一個大小為5的記憶體空間
11         m_size = 0;                      //初始size為0
12         m_capacity = capacity;           //初始的大小為5
13     }
14     int Size()                    //已存元素的個數
15     {
16         return m_size;
17     }
18     int Capacity()               //開闢的記憶體大小
19     {
20         return m_capacity;
21     }
22     void Clear()                  //清空陣列
23     {
24         delete []m_buffer;        //刪除陣列
25         m_size = 0;               //元素個數清0
26         cout<<"Arrary cleared!"<<endl;
27     }
28     void PushBack(T val)     //新增元素
29     {
30         if(m_size >= m_capacity)  //如果陣列已存的元素個數大於開闢的空間,就開闢記憶體
31         {
32             cout<<"Expand the Memory!"<<endl;
33             m_capacity = m_capacity + 2;   //擴大兩個元素的大小
34             T *buffer = new T[m_capacity];  //開闢一個新的空間
35             memcpy(buffer,m_buffer,m_capacity * sizeof(T));  //把舊的陣列的元素拷貝一下
36             delete []m_buffer;   //刪除舊的陣列
37             m_buffer = buffer;    //讓陣列指標指向新陣列
38         }
39         m_buffer[m_size++] = val;   //把val的值新增到陣列中,陣列已存長度+1
40     }
41     void printf()                  //輸出陣列中的元素
42     {
43         T  *ptr = m_buffer;       //定義一個T型別的指標和下面的變數n,用來遍歷陣列
44         int n = m_size;           
45         for(int i = 0; i < n;i++)
46         {
47             cout<<ptr[i]<<" ";
48         }
49         cout<<endl;
50     }
51 private:
52     T *m_buffer;             //陣列指標
53     int m_capacity;              //陣列記憶體大小
54     int m_size;                  //陣列已存元素個數
55 };
56 int main(int argc, char const *argv[])
57 {
58     Myarry<char> b;
59     b.PushBack('a');     //新增五個元素
60     b.PushBack('b');
61     b.PushBack('z');
62     b.PushBack('j');
63     b.PushBack('f');
64     cout<<"Capacity:"<<b.Capacity()<<endl;
65     cout<<"Size:"<<b.Size()<<endl;
66     b.PushBack('z');
67     b.printf();
68     b.Clear();
69     cout<<"Capacity:"<<b.Capacity()<<endl;
70     cout<<"Size:"<<b.Size()<<endl;
71     return 0;
72 }