C++之模板,模板

來源:互聯網
上載者:User

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{};//ERRORtemplate<typename T,floatIn>class A{};//ERRORtemplate<typename T,B In>classA{};//ERRORtemplate<typename T,double&In>class A{};//OKtemplate<typename T,double*In>class A{};//OKtemplate<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>//ERRORvoid foo(){}

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

 

template<typename T=int,typename T1>//ERRORclass 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. 第二個型別參數是inttemplate<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);了。


C語言寫一模板函數,任意兩個數之與,並在主函數(main)中進行顯式與隱式調用

代碼如下:

#include <iostream>
using namespace std;
// 加法的模板函數
template <typename T>
T Add(T a, T b)
{
return (a + b);
}
// 測試函數
int main()
{
// 顯式
cout<<"顯式調用:"<<endl;
int ia = 1, ib = 2, ic = 0;
ic = Add<int>(ia, ib);
cout<<ia<<" + "<<ib<<" = "<<ic<<endl;
// 隱式
cout<<"隱式調用:"<<endl;
float fa = 1.1f, fb = 2.2f, fc = 0.0f;
fc = Add(fa, fb);
cout<<fa<<" + "<<fb<<" = "<<fc<<endl;
cout<<endl;
return 0;
}
測試結果:
 
分層抽樣ABC三層,個體之比5:3:2,從總體抽取20個樣本,C層甲乙都抽到機率1/28則總體個數是哪個

題目給的數有點問題。
做法是這樣的,從c層抽4個,設c層共有n個。
答案不是整數。



 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.