在C++中,模闆類最主要是解決這樣的一個問題:多個類的功能相同,隻是由于資料類型不同而不得不重複定義幾個相似的類,導緻無用的工作量增大和代碼量增多。
模闆定義:模闆就是實作代碼重用機制的一種工具,它可以實作類型參數化,即把類型定義為參數,進而實作了真正的代碼可重用性。模版可以分為兩類,一個是函數模版,另外一個是類模版。
具體執行個體:實作對兩個整數比較、兩個浮點數比較。則我們要重複定義下面兩個類
class Compare_int class Compare_folat
{ {
public : public :
Compare(int iA,int iB) Compare(float fA a,float fB)
{ {
m_iA = a; m_fA = fA ;
m_iB = b; m_fB = fB;
} }
int max(){return (m_iA > m_iB ) ? m_iA : m_iB ;} int max(){return (m_fA > m_fB ) ? m_fA : m_fB ;}
int min(){return (m_iA < m_iB ) ? m_iA :m_iB ;} int min(){return (m_fA < m_fB ) ? m_fA :m_fB ;}
private: private:
int m_ix; int m_fA ;
int m_iy; int m_fB ;
} }
從上面兩個類看出隻有資料類型不同,其他的代碼基本上是一樣的。
有了模闆之後,就可以解決上面重複定義幾個相同功能,但是資料類型不同的類的問題。
可以聲明一個通用的類模闆,它可以有一個或多個虛拟的類型參數,如對以上兩個類可以綜合寫出以下的類模闆:
template <class或typename numtype> //聲明一個模闆,虛拟類型名為numtype。
class Compare //類模闆名為Compare
{
public :
Compare(numtype a,numtype b)
{m_x = a; m_y = b;}
numtype max( )
{return (m_x > m_y) ? m_x : m_y;}
numtype min( )
{return (m_x < m_y) ? m_x : m_y;}
private :
numtype m_x, m_y;
};
将此類模闆和前面第一個Compare_int類作一比較,可以發現有兩處不同:
-
聲明類模闆時要增加一行
template <class 類型參數名>
-
原有的類型名int換成虛拟類型參數名numtype。
在建立類對象時,如果将實際類型指定為int型,編譯系統就會用int取代所有的numtype,如果指定為float型,就用float取代所有的numtype。這樣就能實作“一類多用”。
由于類模闆包含類型參數,是以又稱為參數化的類。如果說類是對象的抽象,對象是類的執行個體,則類模闆是類的抽象,類是類模闆的執行個體。
由于類模闆可以建立各種資料類型的類。在聲明了一個類模闆後,如何通過它建立執行個體。
先回顧一下用具體類來定義對象的方法:
Compare_int cmp1(4,7); // Compare_int是已聲明的類
用類模闆定義對象的方法與此相似,但是不能直接寫成
Compare cmp(4,7); // Compare是類模闆名
Compare是類模闆名,而不是一個具體的類,類模闆體中的類型numtype并不是一個實際的類型,隻是一個虛拟的類型,無法用它去定義對象。
必須用實際類型名去取代虛拟的類型,具體的做法是:
Compare <int> cmp(4,7);
即在類模闆名之後在尖括号内指定實際的類型名,在進行編譯時,編譯系統就用int取代類模闆中的類型參數numtype,這樣就把類模闆具體化了,或者說執行個體化了。這時Compare<int>就相當于前面介紹的Compare_int類。
還有一個問題要說明: 上面列出的類模闆中的成員函數是在類模闆内定義的。如果改為在類模闆外定義,不能用一般定義類成員函數的形式:
numtype Compare::max( ) {…} //不能這樣定義類模闆中的成員函數
而應當寫成類模闆的形式:
template <class numtype>
numtype Compare<numtype>::max( )
{{return (x>y)?x:y;}
歸納以上的介紹,可以這樣聲明和使用類模闆:
- 先寫出一個實際的類。由于其語義明确,含義清楚,一般不會出錯。
- 将此類中準備改變的類型名(如int要改變為float或char)改用一個自己指定的虛拟類型名(如上例中的numtype)。
-
在類聲明前面加入一行,格式為
template <class 虛拟類型參數>,如
template <class numtype> //注意本行末尾無分号
class Compare
{…}; //類體
-
用類模闆定義對象時用以下形式:
類模闆名<實際類型名> 對象名;
類模闆名<實際類型名> 對象名(實參表列);
如
Compare<int> cmp;
Compare<int> cmp(3,7);
-
如果在類模闆外定義成員函數,應寫成類模闆形式:
template <class 虛拟類型參數>
函數類型 類模闆名<虛拟類型參數>::成員函數名(函數形參表列) {…}
關于類模闆的幾點說明:
-
類模闆的類型參數可以有一個或多個,每個類型前面都必須加class,如
template <class T1,class T2>
class someclass
{…};
在定義對象時分别代入實際的類型名,如
someclass<int,double> obj;
- 和使用類一樣,使用類模闆時要注意其作用域,隻能在其有效作用域内用它定義對象。
- 模闆可以有層次,一個類模闆可以作為基類,派生出派生模闆類。
- 模闆類繼承後無法直接調用基類成員,需要使用this->修飾符來通路基類成員
在有些編譯器中類模闆不能分開檔案寫,隻能把定義和具體實作放在同一個檔案中。(因為模闆類在編譯時就相當于宏定義,分兩個檔案是找不到的)
附加:如果模闆函數和函數重載同時使用時,則優先考慮函數重載,再考慮模闆函數,最後考慮類型轉換。