深入淺出理解:函式模板與類模板、存在的實際意義以及使用方法,
在講模板之前,先閒扯一下吧:C++最重要的特性之一就是,程式碼的重用,為了實現程式碼重用,程式碼就必須具有通用性。
通用程式碼不受資料型別的影響,並且可以自動適應資料型別的變化,這種程式設計型別稱為引數化程式設計。
模板是C++支援引數化程式設計的工具,通過它可以實現引數化多型性。
所謂引數化多型性是指:將程式所處理的物件的型別引數化,使得一段程式可以處理多種不同型別的物件。
簡單的理解,模板就是為了讓程式碼的通用性更強。有了以上的理解, 下面理解函式模板和類模板就輕鬆多了。
(2)函式模板
要講函式模板,就得先講一講函式過載,相信大家都知道,函式過載通常是對於不同的資料型別完成類似的操作。
在很多時候,一個演算法其實是可以處理多種資料型別的。但是用函式實現演算法時,即使設計為過載函式也只是
使用相同的函式名,函式體仍然要分別定義。下面舉一個求絕對值的函式過載的例項,方便大家理解。
int abs(int x)
{
return x<0?-x:x;
}
double abs(double x)
{
return x<0?-x:x;
}
可以看出,這兩個函式的功能和函式體是一模一樣的,但是為了實現對不同資料型別的資料求絕對值操作,我們不得不寫兩個
同名函式來實現操作,如果能寫一段通用程式碼適用與多種不同資料型別該多好呢,這會使程式碼的可重用性大大提高,從而提高
軟體的開發效率。所以聰明的人們便設計出了這麼一個工具,那就是函式模板,程式設計師只需對函式模板編寫一次,然後基於調
用函式時提供的引數型別,C++編譯器將自動產生相應的函式來正確處理該型別的資料。
函式模板的定義形式如下:
template<模板引數列表>
型別名 函式名(引數列表)
{
函式體的定義;
}
說明:class或者typename識別符號,指明可以接收一個型別引數,這些型別引數代表的是型別,可以是系統預定義的資料型別,也
可以是自定義型別。型別引數可以用來指定函式模板本身的形參型別、返回值型別,以及宣告函式中的區域性變數。
下面舉一個求絕對值的函式模板的程式。
#include<iostream>
using namespace std;
template<typename T>
T abs(T x)
{
return x<0?-x:x;
}
int main()
{
int n_number=-5;
double d_number=-5.5;
cout<<abs(n_number)<<ebdl;
cout<<abs(d_number)<<endl;
return 0;
}
執行結果如下:
在main函式中兩次呼叫abs()函式,
對於呼叫表示式abs(n_number)時,由於n_number為int型別,所以推匯出模板中型別引數T為int;
當呼叫表示式abs(d_number)時,由於d_number為double型別,所以推匯出模板型別引數T為double;
當型別引數的含義確定後,編譯器將以函式模板作為一個樣板,生成一個函式,這一過程便是傳說中的函式模板例項化;
其實編譯器會例項化以下兩個函式:
int abs(int x)
{
return x<0?-x:x;
}
double abs(double x)
{
return x<0?-x:x;
}
下面來一個稍微複雜一點的函式模板的例子,供各位深入理解一下函式模板的用法。#include<iostream>
using namespace std;
template<class T>
void outputArray(const T *array,int count)
{
for(int i=0;i<count;i++)
{
cout<<array[i]<<" ";
}
cout<<endl;
}
int main()
{
const int a_count=8,b_count=8,c_count=20;
int a[a_count]={1,2,3,4,5,6,7,8};
double b[b_count]={1.1,2.2,3.3,4.4,5.5,6.6,7.7,8.8};
char c[c_count]="Welcome to see you!";
cout<<"陣列a:"<<endl;
outputArray(a,a_count);
cout<<"陣列b:"<<endl;
outputArray(b,b_count);
cout<<"陣列c:"<<endl;
outputArray(c,c_count);
return 0;
}
執行效果如下圖:
(3)類模板
類模板可以使使用者為類定義一種模式,使得類中的某些資料成員、成員函式、返回值或者區域性
變數能取任意型別。(包括系統預定義和使用者自定義的)
如果說類是對一組物件公共性質的抽象,而類模板則是對不同類的公共性質的抽象,所以說類模板是更高層次的抽象。
類模板宣告的語法形式:
template<模板引數表>
class 類名
{
類成員宣告;
}
其中類成員的宣告方法與普通類的宣告方法其實是一模一樣的,只是它的各個成員(資料成員和函式成員)通常要用到模板的型別引數T;
注意:如果要在類模板以外定義成員函式,要採用以下形式
template <模板引數表>
型別名 類名<模板引數識別符號列表>::函式名(引數表)
一個類模板宣告,它自身並不是一個類,它說明類的一個家族,只有當它被其它程式碼引用時,模板才根據需要生成具體的類。
使用一個模板類來建立物件時,格式如下:
模板名<模板引數表>物件名1,....物件名N;
下面舉個例子,方便大家理解,
本例中,宣告一個實現任意型別資料存取的類模板Store,然後通過具體資料型別引數對類模板進行例項化,
生成類,然後類再被例項化,
生成物件s1,s2,s3,d.
源程式如下:
執行結果:#include<iostream> #include<cstdlib> using namespace std; struct Student { int id; float gpa; }; template<class T> class Store { private: T item; bool haveValue; public: Store(); T& getElem(); void putElem(const T &x); }; template<class T> Store<T>::Store():haveValue(false){} template<class T> T& Store<T>::getElem() { if(!haveValue) { cout<<"沒有資料"<<endl; exit(1); } return item; } template<class T> void Store<T>::putElem(const T &x) { haveValue=true; item=x; } int main() { Store<int> s1,s2; s1.putElem(3); s2.putElem(-7); cout<<s1.getElem()<<" "<<s2.getElem()<<endl; Student g={101011,99}; Store<Student>s3; s3.putElem(g); cout<<"The student ID is: "<<s3.getElem().id<<endl; Store<double>d; cout<<d.getElem()<<endl; return 0; }