C++ 模板總結(jié),很全面!
來自:CSDN,作者:千里修行
鏈接:https://blog.csdn.net/zhoumin4576/article/details/103602324
前言:
函數(shù)模板 針對僅參數(shù)類型不同的函數(shù);
類模板 針對僅數(shù)據(jù)成員和成員函數(shù)類型不同的類.
一、函數(shù)模板:
?template? ?
?返回類型?函數(shù)名(參數(shù)列表)
{
??????函數(shù)體
}
template? ?void?swap(T&?a,?T&?b){},
二、類模板:
template
class?類名{?...?};
??template ?class?A
??{
???????public:?
???????T?a;
???????T?b;?
???????T?hy(T?c,?T?&d);
??};
template<模板形參列表>?函數(shù)返回類型?類名<模板形參名>::函數(shù)名(參數(shù)列表){函數(shù)體}
template ?void?A ::h(){}。
三、模板的非類型形參:
1、非類型模板形參:模板的非類型形參也就是內(nèi)置類型形參,如 template
class B{} ; 其中int a就是非類型的模板形參。 2、 非類型形參在模板定義的內(nèi)部是常量值,也就是說非類型形參在模板的內(nèi)部是常量。
3、非類型模板的形參只能是整型,指針和引用,像 double,String, String ** 這樣的類型是不允許的。但是 double &,double *,對象的引用或指針是正確的。
4、調(diào)用非類型模板形參的實參必須是一個常量表達式,即他必須能在編譯時計算出結(jié)果。
5、注意:任何局部對象,局部變量,局部對象的地址,局部變量的地址都不是一個常量表達式,都不能用作非類型模板形參的實參。全局指針類型,全局變量,全局對象也不是一個常量表達式,不能用作非類型模板形參的實參。
6、全局變量的地址或引用,全局對象的地址或引用 const 類型變量是常量表達式,可以用作非類型模板形參的實參。
7、sizeof 表達式的結(jié)果是一個常量表達式,也能用作非類型模板形參的實參。
8、當模板的形參是整型時調(diào)用該模板時的實參必須是整型的,且在編譯期間是常量,比如 template
class A{}; 如果有 int b,這時 A m;將出錯,因為 b 不是常量,如果 const int b,這時 A m; 就是正確的,因為這時 b 是常量。 9、非類型形參一般不應(yīng)用于函數(shù)模板中,比如有函數(shù)模板 template
void h(T b){} ,若使用 h(2) 調(diào)用會出現(xiàn)無法為非類型形參 a 推演出參數(shù)的錯誤,對這種模板函數(shù)可以用顯示模板實參來解決,如用 h (2) 這樣就把非類型形參 a 設(shè)置為整數(shù) 3。顯示模板實參在后面介紹。 10、非類型模板形參的形參和實參間所允許的轉(zhuǎn)換:
允許從數(shù)組到指針,從函數(shù)到指針的轉(zhuǎn)換。如:template
class A{}; int b[1]; A m;即數(shù)組到指針的轉(zhuǎn)換 const 修飾符的轉(zhuǎn)換。如:template
class A{}; int b; A<&b> m; 即從 int * 到 const int * 的轉(zhuǎn)換。 提升轉(zhuǎn)換。如:template class A{}; const short b=2; A m; 即從 short 到 int 的提升轉(zhuǎn)換
整值轉(zhuǎn)換。如:template class A{}; A<3> m; 即從 int ?到 unsigned int 的轉(zhuǎn)換。
常規(guī)轉(zhuǎn)換。
四、類模板的默認模板類型形參:
1、可以為類模板的類型形參提供默認值,但不能為函數(shù)模板的類型形參提供默認值。函數(shù)模板和類模板都可以為模板的非類型形參提供默認值。
2、類模板的類型形參默認值形式為:template
class A{};為第二個模板類型形參T2提供int型的默認值。 3、 類模板類型形參默認值和函數(shù)的默認參數(shù)一樣,如果有多個類型形參則從第一個形參設(shè)定了默認值之后的所有模板形參都要設(shè)定默認值,比如 templateclass A{} ;就是錯誤的,因為 T1 給出了默認值,而T2沒有設(shè)定。
4、 在類模板的外部定義類中的成員時 template 后的形參表應(yīng)省略默認的形參類型。比如 template
class A{public: void h();} ; 定義方法為template void A ::h(){}。
五、模板的實例化:
[cpp]?view?plaincopyprint?
template???
void?swap(T?&a,?T?&b){??
...??
}??
1、隱式實例化:
[cpp]?view?plaincopyprint?
int?main(){??
????....??
????swap(a,b);??
????....??
}??
2、顯式實例化:
[cpp]?view?plaincopyprint?
template?void?swap(int?&a,int?&b);??
3、特化:
[cpp]?view?plaincopyprint?
template?<>?void?swap(job?a,job?b){...}??
六、模板的特化(具體化)和偏特化:
類模板:
測試代碼如下:
#include?
using?namespace?std;
template
class?Test{
public:
????Test(T1?i,T2?j):a(i),b(j){cout<<"模板類"<private:
????T1?a;
????T2?b;
};
template<>???//全特化,由于是全特化,參數(shù)都指定了,參數(shù)列表故為空。
class?Test{
public:
????Test(int?i,char?j):a(i),b(j){cout<<"全特化"<private:
????int?a;
????int?b;
};
template?//由于只指定了一部分參數(shù),剩下的未指定的需在參數(shù)列表中,否則報錯。
class?Test{
public:
????Test(char?i,T2?j):a(j),b(j){cout<<"個數(shù)偏特化"<private:
????char?a;
????T2?b;
};
template?//這是范圍上的偏特化
class?Test{
public:
????Test(T1*?i,T2*?j):a(i),b(j){cout<<"指針偏特化"<private:
????T1*?a;
????T2*?b;
};
template//同理這也是范圍上的偏特化
class?Test{
public:
????Test(T1?i,T2?j):a(i),b(j){cout<<"const偏特化"<private:
????T1?a;
????T2?b;
};
int?main()
{
????int?a;
????Test?t1(0.1,0.2);
????Test?t2(1,'A');
????Test?t3('A',true);
????Test?t4(&a,&a);
????Test?t5(1,2);
????return?0;
}

函數(shù)模板:
#include?
using?namespace?std;
//模板函數(shù)
template
void?fun(T1?a,T2?b){
????cout<<"模板函數(shù)"<}
//全特化
template<>
void?fun(int?a,char?b){
????cout<<"全特化"<}
//函數(shù)不存在偏特化,以下代碼是錯誤的
/*
template
void?fun(char?a,T2?b){
????cout<<"偏特化"<}
*/
int?main()
{
????int?a=0;
????char?b='A';
????fun(a,a);
????fun(a,b);
????return?0;
}

七、模板類的繼承:
1、普通類繼承模板類)
2、(模板類繼承了普通類(非常常見))
3、類模板繼承類模板
4、模板類繼承類模板,即繼承模板參數(shù)給出的基類
1?template
2?class?TBase{
3?????T?data;
4?……
5?};
6?class?Derived:public?TBase{
7?……
8?};
1?class?TBase{
2?……
3?};
4?template
5?class?TDerived:public?TBase{
6?T?data;
7?……
8?};
?1?template
?2?class?TBase{
?3?T?data1;
?4?……
?5?};
?6?template
?7?class?TDerived:public?TBase{
?8?T2?data2;
?9?……
10?};
#include
using?namespace?std;
class?BaseA{
public:
????BaseA(){cout<<"BaseA?founed"<};
class?BaseB{
public:
????BaseB(){cout<<"BaseB?founed"<};
template
class?BaseC{
private:
????T?data;
public:
????BaseC():data(rows){
????????cout<<"BaseC?founed?"<};
template
class?Derived:public?T{
public:
????Derived():T(){cout<<"Derived?founed"<};
void?main()
{
????Derived?x;//?BaseA作為基類
????Derived?y;//?BaseB作為基類
????Derived?>?z;?//?BaseC 作為基類
????
}
八、模板實例化問題:
1、聲明一個類模板的指針和引用,不會引起類模板的實例化,因為沒有必要知道該類的定義
2、定義一個類類型的對象時需要該類的定義,因此類模板會被實例化
3、在使用 sizeof() 時,它是計算對象的大小,編譯器必須根據(jù)類型將其實例化出來,所以類模板被實例化.
4、 new 表達式要求類模板被實例化。
5、引用類模板的成員會導(dǎo)致類模板被編譯器實例化
6、需要注意的是,類模板的成員函數(shù)本身也是一個模板。標準 C++ 要求這樣的成員函數(shù)只有在被調(diào)用或者取地址的時候,才被實例化。用來實例化成員函數(shù)的類型,就是其成員函數(shù)要調(diào)用的那個類對象的類型。
