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 }