C++之模板

C++模版分爲函數模版和類模版。其實模版就是一種對類型進行參數化的工具。
 

一、函數模板

1. 函數模板的聲明定義

Template<typename/class T1,typename/class T2,….>

返回類型  函數名(函數參數){

     函數體

}

例如:

template<typename T>
T add(T t,T t1)
{
       return t+t1;
}

2. 模板函數的兩種調用方式

(1)隱式調用

       int i=1,j=2;
       int k=add(i,j);

默認是add函數的兩個參數是同一類型,系統根據實際調用的時候的實參的類型來確定形參的具體類型。注意這裏的兩個參數類型必須相同。

不可以:

       int i=1,j=2;
       int k=add(1,2.2);//error

(2)顯示調用

       int i=1,j=2;
       int k=add<int>(i,j);

首先指定函數模板的具體類型,再去調用。這樣就可以:

       int k=add<int>(1,2.3);//OK
這裏和隱式調用的區別:顯示調用就是先指定函數模板的具體類型,則函數就變成了具體類型的函數了,intadd(int t,int t1)則,再去調用add<int>(1,2.3);顯然就沒有問題了。只是系統進行了一下類型轉換而已。

 

二、類模板

1. 類模板的聲明定義

Template<typename/class T1, typename/class T2,….>

Class 類名{

類的成員

}

例如:

template<typename T>
class A
{
public:A(T b):a(b){}
          T swap(T& i,T& j){
                 T temp;
                 temp=i;
                 i=j;
                 j=temp;
          }
          void foo(Tt);
private: T a;
};
template<typename T>
void A<T>::foo(T t)
{cout<<t<<endl;}

(1) 注意類模板的成員函數,如果在類外實現,必須在實現的前面加上template<…>。而且函數名前加”類名<T1[,T2,…]>”。

 

(2)當在類中,想要使用自身類的對象時(例如:copy構造、operator=)時,直接使用類名就可以了。例如:

class A
{
public:A(T b):a(b){}
          A(const A&c){
                 a=c.a;
          }
private: T a;
};

(3)當在類外,想要使用這個類名的時候,必須要“類名+<T,….>”。例如成員函數的類外實現的時候,函數名前加的東西。

class A
{
public:A(T b):a(b){}
          void foo(Ac);
private: T a;
};
template<typename T>
void A<T>::foo(A<T>t)
{cout<<t<<endl;}

(4)有時在類中,想使用同一模板的其他類型的類的對象。例如:

class A
{
public:A(T b):a(b){}
          T getm(){returna;}
          template<typename Y>
          voidfoo(A<Y>& c){
                 cout<<c.getm()<<endl;
          }
private: T a;
};
int _tmain(int argc, _TCHAR* argv[])
{
       A<int> a(2);
       A<double> b(3);
       a.foo(b);
       system("pause");
       return 0;
}


在函數的前面加template<typenameY>,注意這裏的Y不能和類模板時的T相同。

 

這種情況常常在重寫copy和operator=時,使用子類的對象給父類的對象copy構造、賦值時,會經常用到這個。具體可見auto_ptr的源碼。

 

2. 類模板的調用

         A<int>a(2);

定義類對象時,先聲明類的具體類型。

 

(1) 和函數模板隱式調用不一樣的地方,可以:

template<typename T>
class A
{
public:A(T b):a(b){}
          T add(T t1, T t2)
          {returnt1+t2;}
private: T a;
};
int _tmain(int argc, _TCHAR* argv[])
{
       A<int> a(2);
       a.add(2,3.4);//OK
       system("pause");
       return 0;
}

這裏注意類模板的原理:由於類模板在定義的時候,已經確定了T的類型,所以在定義了A<int> a(2)之後,a中的T都變成了int,所以這裏add函數就變成了int add(int t1,int t2).這顯然就可以add(2,3.4)調用了,符合系統的默認類型轉換。

 

三、非類型形參

當template<typenameT,int In>。出現了int這樣的具體類型的時候,In就是非類型形參。例如:

template<typename T,int In>
class A
{
public:A(){a=new T[In];a[0]=3;}
          void foo()
          {cout<<a[0]<<endl;}
          ~A(){delete[]a;}
private: T* a;
};
int _tmain(int argc, _TCHAR* argv[])
{
       A<int,4> a;
       a.foo();
       system("pause");
       return 0;
}

1. 注意非類型形參In在內部是常量值,在上面代碼中也可以看出。

2. 非類型形參只能是整型(包括int,short,unsigned int),指針和引用。就是說不可以是float/double/string,但可以是double*/double&/對象的指針、引用也是可以的。

class B{};
template<typename T,doubleIn>class A{};//ERROR
template<typename T,floatIn>class A{};//ERROR
template<typename T,B In>classA{};//ERROR
template<typename T,double&In>class A{};//OK
template<typename T,double*In>class A{};//OK
template<typename T,B* In>classA{};//OK

3. 調用非類型形參的實參必須是一個常量表達式。編譯時能計算出結果。(注意sizeof的返回值也是常量,可以使用)

注意:何爲常量表達式?(值不會改變,且在編譯階段就能知道確切值的)

const int 、常數、枚舉。全局變量的引用/地址、全局對象的引用/地址。

不爲常量表達式:

局部變量,局部對象,局部對象的地址/引用,全局變量,全局對象都不是常量表達式。

 

總結:首先非類型的形參只能是整型,指針,引用。這也決定了實參也必須只能是這些,除此之外,實參還必須是一個常量表達式。所以這樣一彙總:實參只能是const int、常數、枚舉、全局變量的引用/地址、全局對象的引用/地址。

 

也可以:

template<int* T>
class A{};
int d[]={2};
int _tmain(int argc, _TCHAR* argv[])
{
       A<d>a;//----------OK
       system("pause");
       return 0;
}

4. 有非類型形參的模板在類外實現成員函數時:

其實對於所有類型的類模板,在類外面定義類的成員時template後面的模板形參應與要定義的類的模板形參一致

template<int* a>
class A{
public:void foo();
};
template<int* a>  -----在類外不要隨便修改a的名稱。保持和定義時一致
void A<a>::foo(){}

四、可以爲類模板提供類型的默認值

 

template<typename T=int>
class A{};
int _tmain(int argc, _TCHAR* argv[])
{
       A<>a;//OK
       A<double> b;//OK
       system("pause");
       return 0;
}

1. 注意可以爲類模板提供類型默認值,不可以爲函數模板提供類型默認值。

template<typename T=int>//ERROR
void foo(){}

2. 注意當有多個類型形參,則從第一個提供默認值的形參後面的形參,都要提供默認值。

 

template<typename T=int,typename T1>//ERROR
class A{};

五、模板的特化

1. 模板的特化就是給一個已存在的模板進行特殊類型時,執行特別的操作。

 

template<typename T>
class A{
public:void foo(){cout<<"T"<<endl;}
};
template<>//模板的特化,當T爲int時,執行的是這個,當其他類型時,執行的是上面
class A<int>{
public:void foo(){cout<<"int"<<endl;}
};
int _tmain(int argc, _TCHAR* argv[])
{
       A<int> a;//執行的是下面那個特化模板
       a.foo();
       A<double> b;//執行的是上面那個普通模板
       b.foo();
       system("pause");
       return 0;
}

2. 模板的偏特化,也叫部分特化。當模板有多個形參時,對其中一部分進行特化。與上面的全部特化想對應。

//1. 標準模板類。
template<typename T1, typenameT2>
class MyClass {
       ... ...
};
//2. 兩個模板參數具有相同類型的部分特化類。
template<typename T>
class MyClass<T,T> {
       ... ...
};
//3. 第二個類型參數是int
template<typename T>
class MyClass<T,int> {
       ... ...
};
//4. 兩個模板參數都是指針。
template<typename T1,typenameT2>
classMyClass<T1*,T2*> {
       ... ...
};
//5. 兩個模板參數都是相同類型的指針。
template<typename T>
class MyClass<T*,T*>{
       ... ...
};
//6. 模板的全部特化,上面2~5爲部分特化
template<>
class MyClass<int,double>{
       ......
};
int _tmain(int argc, _TCHAR* argv[])
{
   MyClass<int,float>c1;         //調用MyClass<T1,T2>
   MyClass<float,float> c2;    //調用MyClass<T,T>
   MyClass<float,int> c3;      //調用MyClass<T,int>
   MyClass<int*,float*>c4;    //調用MyClass<T1*,T2*>
   MyClass<int*,int*>c5;      //調用MyClass<T*,T*>
       MyClass<int,double>c6;      //調用MyClass<int,double>
       system("pause");
       return 0;
}


全部特化一般都是template<>。中括號裏沒有東西,把所有的形參全部特化。

部分特化一般都是template<T,…>。中括號裏有東西,沒有把所有的形參都特化。

 

函數模板同理也可以特化。

 

六、函數模板的重載

類模板是不可以重載的,而函數模板可以重載。

例子:

template<typename T>
void foo(T)
{cout<<"template"<<endl;}
void foo(int)
{cout<<"foo"<<endl;}
int _tmain(int argc, _TCHAR* argv[])
{
       int i=2;
       double j=2.0;
       foo(i);//調用的是foo(int)------(1)
       foo(j);//調用的是template<>foo(T)----------(2)
       system("pause");
       return 0;
}


和特化類似,只是類模板不可以重載。(廢話,類能重載嗎)

重載選擇的順序:“越特殊越優先高”

顯然如果(1)這麼寫的話:”foo<int>(i);”就是顯式的調用函數模板,這樣就調用的tempalte<>foo(T);了。

發佈了45 篇原創文章 · 獲贊 32 · 訪問量 10萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章