C++中的模板那點事


1.什么是模板

假設現在我們完成這樣的函數,給定兩個數x和y求式子x^2 + y^2 + x * y的值 .考慮到x和y可能是 int , float 或者double類型,那么我們就要完成三個函數:

int fun(int x,int y);

float fun(float x,float y);

double fun(double x,double y);

並且每個fun函數內部所要完成的操作也是極其的相似。如下:

View Code
 1 int fun(int x,int y)
 2 {
 3     int tmp = x *x + y * y + x * y;
 4     return tmp;
 5 }
 6 float fun(float x,float y)
 7 {
 8     float tmp = x *x + y * y + x * y;
 9     return tmp;
10 }
11 double fun(double x,double y)
12 {
13     double tmp = x *x + y * y + x * y;
14     return tmp;
15 }

可以看出,上面的三個函數體除了類型不一樣之外,其他的完全一樣,那么如果能夠只寫一個函數就能完成上面的三個函數的功能該多好呢?如果從這三個函數提煉出一個通用函數,而它又適用於這三種不同類型的數據,這樣會使代碼的重用率大大提高。實際上C++中的模板正好就是來解決這個問題的。模板可以實現類型的參數化(把類型定義為參數),從而實現了真正的代碼可重用性。C++中的模板可分為函數模板和類模板,而把函數模板的具體化稱為模板函數,把類模板的具體化成為模板類。下面讓我們分別看看什么是函數模板和類模板吧~~~

2.模板函數

實際上我們利用函數模板,只需要一個函數就可能完成上面的三個函數了,千言萬語不如看代碼: 

View Code
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 template <typename T>
 6 T fun(T x,T y)
 7 {
 8     T tmp = x *x + y * y + x * y;
 9     return tmp;
10 }
11 int main()
12 {
13     int x1 = 1,y1 = 4;
14     float x2 = 1.1 , y2 = 2.2;
15     double x3 = 2.0 , y3 = 3.1;
16     cout<<fun(x1,y1)<<endl;
17     cout<<fun(x2,y2)<<endl;
18     cout<<fun(x3,y3)<<endl;
19     return 0;
20 }

運行結果:

如此利用模板,我們很輕而易舉的達到了我們的目的,而這也大大的提高了代碼的可重用性,這也讓我們想起了STL中的那些算法了吧,這些算法使用多種的數據類型。實際上STL即使模板的重要應用了。

現在我們想,如果上面的代碼這樣調用fun(x1,y2)會怎么樣呢?點擊編譯會出現這樣的錯誤:

可以看到編譯編譯出現問題的是fun(x1,y2),說的意思就是沒有對應的函數,要么x1和y2都是int型,要么x1和y2都是float型。那么我為什么要說一下這樣一種情況呢?主要是為了引出模板也可以同時使用兩個:

View Code
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 
 6 template <typename T1 , typename T2>
 7 T2 fun(T1 x,T2 y)
 8 {
 9     T2 tmp = x *x + y * y + x * y;
10     return tmp;
11 }
12 int main()
13 {
14     int x1 = 1,y1 = 4;
15     float x2 = 1.1 , y2 = 2.2;
16     double x3 = 2.0 , y3 = 3.1;
17     cout<<fun(x1,y1)<<endl;
18     cout<<fun(x2,y2)<<endl;
19     cout<<fun(x3,y3)<<endl;
20     cout<<fun(x1,y2)<<endl;
21     return 0;
22 }

運行結果:

當使用兩個模板時,為什么fun(x1,y1)也能正確運行呢?因為當進行這個調用時,T1 = int ,T2 = int。所以這種調用也是沒有問題的。

提到函數想到重載是很自然的吧,那么模板函數能不能重載呢?顯然是能的了,還是看代碼:

View Code
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 
 6 template <typename T1 , typename T2>
 7 T2 fun(T1 x,T2 y)
 8 {
 9     cout<<"調用了兩個個參數的 fun 函數 ^^ "<<endl;
10     T2 tmp = x *x + y * y + x * y;
11     return tmp;
12 }
13 template <typename T>
14 T fun(T x , T y , T z)
15 {
16     cout<<"調用了三個參數的 fun 函數 ^^ "<<endl;
17     T tmp = x * x + y * y + z * z + x * y * z; 
18     return tmp;
19 }
20 int main()
21 {
22     int x1 = 1 , y1 = 4 , z1 = 5;
23     float x2 = 1.1 , y2 = 2.2;
24     double x3 = 2.0 , y3 = 3.1;
25     cout<<fun(x1,y1)<<endl;
26     cout<<fun(x2,y2)<<endl;
27     cout<<fun(x3,y3)<<endl;
28     cout<<fun(x1,y2)<<endl;
29     cout<<fun(x1,y1,z1)<<endl;
30     return 0;
31 }

運行結果:

從結果已經能看出來模版函數的重載是沒有任何問題的了。那么模板函數和非模板函數之間是否能夠重載呢??

View Code
 1 #include <iostream>
 2 
 3 using namespace std;
 4 
 5 template <typename T>
 6 T fun(T x,T y)
 7 {
 8     cout<<"調用了模板函數 ^^ "<<endl;
 9     T tmp = x * x + y * y + x * y;
10     return tmp;
11 }
12 int fun(int x,int y)
13 {
14     cout<<"調用了非模板函數 ^^ "<<endl;
15     int tmp = x * x + y * y + x * y;
16     return tmp;
17 }
18 
19 int main()
20 {
21     int x1 = 1 , y1 = 4;
22     float x2 = 1.1 , y2 = 2.2;
23     cout<<fun(x1,y1)<<endl;
24     cout<<fun(x2,y2)<<endl;
25     return 0;
26 }

運行結果:

看以看出模版函數和非模板函數也是可能重載的,那么重載函數的調用順序是怎么樣的呢?實際上是先查找非模板函數,要有嚴格匹配的非模板函數,就調用非模板函數,找不到適合的非模板函數在和模板函數進行匹配。

到這里,關於模板就說這些吧~~~~

3.模板類

要是理解了模版函數,模板類就相當的簡單了,只不過模版函數是對函數中的類型使用模板,而模板類是對類中的類型使用模板,這我就不多說了,下面的代碼是我以前利用模板寫的單鏈表,這個是模板的典型應用:(測試過)

View Code
  1 #include <stdio.h>
  2 #include <iostream.h>
  3 
  4 template <class T>
  5 struct SLNode
  6 {
  7     T data;
  8     SLNode<T> *next;
  9     SLNode(SLNode<T> *nextNode=NULL)
 10     {
 11         next = nextNode;
 12     }
 13     SLNode(const T &item,SLNode<T> *nextNode=NULL)
 14     {
 15         data = item;
 16         next = nextNode;
 17     }
 18 };
 19 
 20 template <class T>
 21 class SLList
 22 {
 23 private:
 24     SLNode<T> *head;
 25     SLNode<T> *tail;
 26     SLNode<T> *currptr;
 27     int size;
 28 public:
 29     SLList();
 30     SLList(const T &item);
 31     ~SLList();
 32     bool IsEmpty()const;
 33     int Length()const;
 34     bool Find(int k,T &item)const;
 35     int Search(const T &item)const;
 36     void InsertFromHead(const T &item);
 37     void InsertFromTail(const T &item);
 38     bool DeleteFromHead(T &item);
 39     bool DeleteFromTail(T &item);
 40     void Insert(int k,const T &item);
 41     void Delete(int k,T &item);
 42     void ShowListMember();
 43 };
 44 //構造函數
 45 template <class T>
 46 SLList<T>::SLList()
 47 {
 48     head = tail = currptr = new SLNode<T>();
 49     size = 0;
 50 }
 51 //構造函數
 52 template <class T>
 53 SLList<T>::SLList(const T &item)
 54 {
 55     tail = currptr = new SLNode<T>(item);
 56     head = new SLNode<T>(currptr);
 57     size = 1;
 58 }
 59 //析構函數
 60 template <class T>
 61 SLList<T>::~SLList()
 62 {
 63      SLNode<T> *temp;
 64     while(!IsEmpty())
 65     {
 66         temp = head->next;
 67         head->next = temp->next;
 68         delete temp;
 69         
 70     }
 71 }
 72 //判斷鏈表是否為空
 73 template <class T>
 74 bool SLList<T>::IsEmpty()const
 75 {
 76     return head->next == NULL;
 77 }
 78 //返回鏈表的長度
 79 template <class T>
 80 int SLList<T>::Length()const
 81 {
 82      return size;
 83 }
 84 //查找第k個節點的閾值
 85 template <class T>
 86 bool SLList<T>::Find(int k,T &item)const
 87 {
 88     if(k < 1)
 89     {
 90         cout<<"illegal position !"<<endl;
 91     }
 92     SLNode<T> *temp = head;
 93     int count = 0;
 94     while(temp != NULL && count < k)
 95     {
 96         temp = temp->next;
 97         count++;
 98     }
 99     if(temp == NULL)
100     {
101         cout<<"The list does not contain the K node !"<<endl;
102         return false;
103     }
104     item = temp->data;
105     return true;
106 }
107 //查找data閾值為item是表的第幾個元素
108 template <class T>
109 int SLList<T>::Search(const T &item)const
110 {
111     SLNode<T> *temp = head->next;
112     int count = 1;
113     while(temp != NULL && temp->data != item)
114     {
115         temp = temp->next;
116         count++;
117     }
118     if(temp == NULL)
119     {
120         cout<<"The node does not exist !"<<endl;
121         return -1;
122     }
123     else
124     {
125         return count;
126     }
127 }
128 //從表頭插入
129 template <class T>
130 void SLList<T>::InsertFromHead(const T &item)
131 {    
132     if(IsEmpty())
133     {
134         head->next = new SLNode<T>(item,head->next);
135         tail = head->next;
136     }
137     else
138     {
139         head->next = new SLNode<T>(item,head->next);
140     }
141     size++;
142 }
143 //從表尾插入
144 template <class T>
145 void SLList<T>::InsertFromTail(const T &item)
146 {
147     tail->next = new SLNode<T>(item,NULL);
148     tail = tail->next;
149     size++;
150 }
151 //從表頭刪除
152 template <class T>
153 bool SLList<T>::DeleteFromHead(T &item)
154 {
155     if(IsEmpty())
156     {
157         cout<<"This is a empty list !"<<endl;
158         return false;
159     }
160     SLNode<T> *temp = head->next;
161     head->next = temp->next;
162     size--;
163     item = temp->data;
164     if(temp == tail)
165     {
166         tail = head;
167     }
168     delete temp;
169     return true;
170 }
171 //從表尾刪除
172 template <class T>
173 bool SLList<T>::DeleteFromTail(T &item)
174 {
175     if(IsEmpty())
176     {
177         cout<<"This is a empty list !"<<endl;
178         return false;
179     }
180     SLNode<T> *temp = head;
181     while(temp->next != tail)
182     {
183         temp = temp->next;
184     }
185     item = tail->data;
186     tail = temp;
187     tail->next=NULL;
188     temp = temp->next;
189     delete temp;
190     size--;
191     return true;
192 }
193 //在第k個節點后插入item值
194 template <class T>
195 void SLList<T>::Insert(int k,const T &item)
196 {
197     if(k < 0 || k > size)
198     {
199         cout<<"Insert position Illegal !"<<endl;
200         return;
201     }
202     if(k == 0)
203     {
204         InsertFromHead(item);
205         return;
206     }
207     if(k == size)
208     {
209         InsertFromTail(item);
210         return;
211     }
212     SLNode<T> *temp = head->next;
213     int count = 1;
214     while(count < k)
215     {
216         count++;
217         temp = temp->next;
218     }
219     SLNode<T> *p = temp->next;
220     temp->next = new SLNode<T>(item,p);
221     size++;
222 }
223 //刪除第k個節點的值,保存在item中
224 template <class T>
225 void SLList<T>::Delete(int k,T &item)
226 {
227     if(k <= 0 || k > size)
228     {
229         cout<<"Ileegal delete position !"<<endl;
230         return;
231     }
232     if(k == 1)
233     {
234         DeleteFromHead(item);
235         return;
236     }
237     if(k == size)
238     {
239         DeleteFromTail(item);
240         return;
241     }
242     SLNode<T> *temp = head->next;
243     int count = 1;
244     while(count < k-1)
245     {
246         count++;
247         temp = temp->next;
248     }
249     SLNode<T> *p = temp->next;
250     temp->next = p->next;
251     p->next = NULL;
252     item = p->data;
253     delete p;
254     size--;
255 }
256 template <class T>
257 void SLList<T>::ShowListMember()
258 {
259     cout<<"List Member : ";
260     SLNode<T> *temp = head->next;
261     while(temp != NULL)
262     {
263         cout<<temp->data<<" ";
264         temp = temp->next;
265     }
266     cout<<endl;
267 }
268 
269 /*
270 1.引入了InsertFronHead,InsertFromTail,DeleteFromHead和DeleteFromTail用來實現
271   Insert和Delete函數,是一個比較好的方法。
272 2.SLNode(T &item,SLNode<T> *nextNode)這個構造函數設計的非常巧妙,便於其他成員
273   函數的實現。
274 3.插入,刪除分為:表頭,表尾,中間插入(刪除)三種情況
275 */
276 
277 
278 
279 int main()
280 {
281     int item;
282     SLList<int> list(12);
283 
284     list.Insert(0,11);
285     cout<<"list number:"<<list.Length()<<endl;
286     list.ShowListMember();
287 
288     list.Insert(2,14);
289     cout<<"list number:"<<list.Length()<<endl;
290     list.ShowListMember();
291 
292     list.Insert(2,13);
293     cout<<"list number:"<<list.Length()<<endl;
294     list.ShowListMember();
295 
296     list.Delete(2,item);
297     cout<<"item = "<<item<<endl;
298     cout<<"list number:"<<list.Length()<<endl;
299     list.ShowListMember();
300 
301     list.Delete(1,item);
302     cout<<"item = "<<item<<endl;
303     cout<<"list number:"<<list.Length()<<endl;
304     list.ShowListMember();
305 
306     list.Delete(2,item);
307     cout<<"item = "<<item<<endl;
308     cout<<"list number:"<<list.Length()<<endl;
309     list.ShowListMember();
310     return 0;
311 }

利用模板的好處是,SLList中的數據可以是任意的數據類型,這也就是泛型編程的概念了吧~~~~

 

 


學習中的一點總結,歡迎拍轉哦^^


 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM