函數(shù)模板
在設(shè)計(jì)程序中的函數(shù)時(shí)赡鲜,可能會(huì)遇到函數(shù)中參數(shù)的類(lèi)型有差異空厌,但需要實(shí)現(xiàn)的功能類(lèi)似的情形。函數(shù)重載可以處理這種情形银酬。重載函數(shù)的參數(shù)表中蝇庭,可以寫(xiě)不同類(lèi)型的參數(shù),從而可以處理不同的情形捡硅。
函數(shù)模板的概念
為了提高效率哮内,實(shí)現(xiàn)代碼復(fù)用,C++提供了一種處理機(jī)制,即使用函數(shù)模板北发。函數(shù)在設(shè)計(jì)時(shí)并不使用實(shí)際的類(lèi)型纹因,而是使用虛擬的類(lèi)型參數(shù)。這樣可以不必為每種不同的類(lèi)型都編寫(xiě)代碼段琳拨。當(dāng)用實(shí)際的類(lèi)型來(lái)實(shí)例化這種函數(shù)時(shí)瞭恰,將函數(shù)模板與某個(gè)具體數(shù)據(jù)類(lèi)型連用。編譯器將以函數(shù)模板為樣板狱庇,生成一個(gè)函數(shù)惊畏,即產(chǎn)生了模板函數(shù),這個(gè)過(guò)程稱(chēng)為函數(shù)模板實(shí)例化密任。函數(shù)模板實(shí)例化的過(guò)程由編譯器完成颜启。程序設(shè)計(jì)時(shí)并不給出相應(yīng)數(shù)據(jù)的類(lèi)型,編譯時(shí)浪讳,由編譯器根據(jù)實(shí)際的類(lèi)型進(jìn)行實(shí)例化缰盏。
函數(shù)模板的示例
#include <iostream>
using namespace std;
template <typename T>
T abs(T x) {
return x < 0 ? -x : x;
};
int main() {
int n = -5;
int m = 10;
double d = -.5;
float f = 3.2;
cout << n << "的絕對(duì)值是:" << abs(n) << endl;//-5的絕對(duì)值是:5
cout << m << "的絕對(duì)值是:" << abs(m) << endl;//10的絕對(duì)值是:10
cout << d << "的絕對(duì)值是:" << abs(d) << endl;//-0.5的絕對(duì)值是:0.5
cout << f << "的絕對(duì)值是:" << abs(f) << endl;//3.2的絕對(duì)值是:3.2
return 0;
};
在主函數(shù)中,調(diào)用abs(n)
時(shí)淹遵,編譯器根據(jù)實(shí)參n
的類(lèi)型int
口猜,推導(dǎo)出模板中的類(lèi)型參數(shù)T
為int
,然后實(shí)例化函數(shù)模板透揣,生成函數(shù)模板abs
的一個(gè)實(shí)例:
int abs(int x) {
return x < 0 ? -x : x;
};
這個(gè)實(shí)例即是模板函數(shù)济炎。
當(dāng)調(diào)用abs(d)
時(shí),根據(jù)實(shí)參d
的類(lèi)型double
辐真,又實(shí)例化一個(gè)新的函數(shù):
double abs(double x) {
return x < 0 ? -x : x;
};
這是另一個(gè)模板函數(shù)须尚。
實(shí)際上,函數(shù)模板不是一個(gè)具體的函數(shù)拆祈,編譯器不能為其生成可執(zhí)行代碼恨闪。定義函數(shù)模板后只是一個(gè)對(duì)函數(shù)功能框架的描述,當(dāng)它具體執(zhí)行時(shí)放坏,將根據(jù)傳遞的實(shí)參決定其功能咙咽。
雖然函數(shù)模板的使用形式與函數(shù)類(lèi)似,但二者有本質(zhì)的區(qū)別淤年,主要表現(xiàn)在以下3個(gè)方面:
- 函數(shù)模板本身在編譯時(shí)不會(huì)生成任何目標(biāo)代碼钧敞,只有當(dāng)通過(guò)模板生成具體的函數(shù)實(shí)例時(shí)才會(huì)生成目標(biāo)代碼。
- 被多個(gè)源文件引用的函數(shù)模板麸粮,應(yīng)當(dāng)連同函數(shù)體一同放在頭文件中溉苛,而不能像偶同函數(shù)那樣只將生命放在頭文件中。
- 函數(shù)指針也只能指向模板的實(shí)例弄诲,而不能指向模板本身愚战。
函數(shù)或函數(shù)模板調(diào)用語(yǔ)句的匹配順序
函數(shù)與函數(shù)模板也是允許重載的娇唯。在函數(shù)和函數(shù)模板名字相同的情況下,一條函數(shù)調(diào)用語(yǔ)句到底應(yīng)該被匹配成對(duì)哪個(gè)函數(shù)或哪個(gè)模板的調(diào)用呢寂玲?C++編譯器遵循以下先后順序塔插。
- 先找參數(shù)完全匹配的普通函數(shù)(不是由模板實(shí)例化得到的模板函數(shù))。
- 再找參數(shù)完全匹配的模板函數(shù)拓哟。
- 然后找實(shí)參經(jīng)過(guò)自動(dòng)類(lèi)型轉(zhuǎn)換后能夠匹配的普通函數(shù)想许。
- 如果上面的都找不到,則報(bào)錯(cuò)断序。
類(lèi)模板
類(lèi)模板概念
通過(guò)類(lèi)模板流纹,可以實(shí)例化一個(gè)個(gè)的類(lèi)。繼承機(jī)制也是在系列的類(lèi)之間建立某種聯(lián)系违诗,這兩種涉及多個(gè)類(lèi)的機(jī)制是有很大差異的漱凝。類(lèi)是相同類(lèi)型事物的抽象,有繼承關(guān)系的類(lèi)可以具有不同的操作较雕。而模板是不同類(lèi)型的事物具有相同的操作碉哑,實(shí)例化后的類(lèi)之間沒(méi)有聯(lián)系挚币,相互獨(dú)立亮蒋。
聲明類(lèi)模板的一個(gè)格式如下:
template <模板參數(shù)表>
class 類(lèi)模板名 {
類(lèi)體定義
};
其中,模板參數(shù)表的形式與函數(shù)模板中的模板參數(shù)表完全一樣妆毕。類(lèi)體定義與普通類(lèi)的定義幾乎相同慎玖,只是在它的成員變量和成員函數(shù)中通常要用到模板的類(lèi)型參數(shù)。
類(lèi)模板的成員函數(shù)既可以在類(lèi)體內(nèi)進(jìn)行說(shuō)明笛粘,也額可以在類(lèi)體外進(jìn)行說(shuō)明趁怔。如果在類(lèi)體內(nèi)定義,則自動(dòng)成為內(nèi)聯(lián)函數(shù)薪前。如果需要在類(lèi)模板以外定義其成員函數(shù)润努,則要采用以下格式:
template <模板參數(shù)表>
返回類(lèi)型名 類(lèi)模板名<模板參數(shù)標(biāo)識(shí)符列表>::成員函數(shù)名(參數(shù)表) {
函數(shù)體
};
類(lèi)模板聲明本身并不是一個(gè)類(lèi),它說(shuō)明了類(lèi)的一個(gè)家族示括。只有當(dāng)被其他代碼引用時(shí)铺浇,模板才根據(jù)引用的需要生成具體的類(lèi)。
不能使用類(lèi)模板來(lái)直接生成對(duì)象垛膝,因?yàn)轭?lèi)型參數(shù)是不確定的鳍侣,必須先為模板參數(shù)指定實(shí)參,即模板要實(shí)例化后吼拥,才可以創(chuàng)建對(duì)象倚聚。也就是說(shuō),當(dāng)使用類(lèi)模板創(chuàng)建對(duì)象時(shí)凿可,要隨類(lèi)模板名給出對(duì)應(yīng)于類(lèi)型形參或普通形參的具體實(shí)參惑折,格式如下:
類(lèi)模板名 <模板參數(shù)表> 對(duì)象名1,...,對(duì)象名n;
//或是
類(lèi)模板名 <模板參數(shù)表> 對(duì)象名1(構(gòu)造函數(shù)實(shí)參),...,對(duì)象名n(構(gòu)造函數(shù)實(shí)參);
編譯器由類(lèi)模板生成類(lèi)的過(guò)程稱(chēng)為類(lèi)模板的實(shí)例化。由類(lèi)模板實(shí)例化得到的類(lèi)稱(chēng)為模板類(lèi)。
要注意的是惨驶,與類(lèi)型形參相對(duì)應(yīng)的實(shí)參是類(lèi)型名矗积。
類(lèi)模板示例
二院組是常用的一種結(jié)構(gòu)〕ㄟ郑可以定義兩個(gè)值的二院組棘捣,如平面坐標(biāo)系下點(diǎn)的橫、縱坐標(biāo)組成的二元組休建。還可以定義兩個(gè)字符串的二元組乍恐,如字典中單詞與釋義組成的二元組。還可以定義學(xué)生姓名及其成績(jī)的二元組测砂。二元組的例子非常多茵烈,不勝枚舉。
如果要定義二元組類(lèi)砌些,則需要根據(jù)組成二元組的類(lèi)型定義很多不同的類(lèi)∥赝叮現(xiàn)在可以使用類(lèi)模板來(lái)解決問(wèn)題。
#include <ostream>
using namespace std;
template <class T>
class TestClass {
public:
T buffer[10];
T getData(int j);
};
template <class T>
T TestClass<T>::getData(int j) {
return *(buffer + j);
};
int main() {
TestClass<char> ClassInstA;//char取代T存璃,從而實(shí)例化出具體的類(lèi)
int i;
char cArr[6] = "abcde";
for (i = 0; i < 5; i++) {
ClassInstA.buffer[i] = cArr[i];
}
for (i = 0; i < 5; i++) {
char result = ClassInstA.getData(i);
cout << result << " ";
}
cout << endl;
TestClass<double> ClassInstF;//實(shí)例化為另外一個(gè)具體的類(lèi)
double fArr[6] = {12.1, 23.2, 34.3, 45.4, 56.5, 67.6};
for (i = 0; i < 6; i++) {
ClassInstF.buffer[i] = fArr[i] - 10;
}
for (i = 0; i < 6; i++) {
double result = ClassInstF.getData(i);
cout << result << " ";
}
cout << endl;
//2.1 13.2 24.3 35.4 46.5 57.6
return 0;
};
類(lèi)模板與繼承
類(lèi)之間允許繼承仑荐,類(lèi)模板之間也允許繼承。具體來(lái)說(shuō)纵东,類(lèi)模板和類(lèi)模板之間粘招、類(lèi)模板和類(lèi)之間可以互相繼承,它們之間的常見(jiàn)派生關(guān)系有以下4中情況:
- 普通類(lèi)繼承模板類(lèi)
- 類(lèi)模板繼承普通類(lèi)
- 類(lèi)模板繼承類(lèi)模板
- 類(lèi)模板繼承模板類(lèi)
根據(jù)類(lèi)模板示例化的類(lèi)即是模板類(lèi)偎球。
#include <iostream>
#include <string>
using namespace std;
template <class T>
class TBase {
public:
T data1;
void Print() {
cout << "TBase::" << data1 << endl;
};
};
template <class T1, class T2>
class TDerievd : public TBase<T1> {
public:
T2 data2;
void Print() {
TBase<T1>::Print();
cout << "TDerived::" << data2 << endl;
};
};
int main() {
TDerived<int, int> d;//類(lèi)模板實(shí)例化洒扎,并聲明對(duì)象d
d.data1 = 5;
d.data2 = 8;
d.Print();
//TBase::5
//TDerived::8
TDerived<string, string> d2;
d2.data1 = "happy";
d2.data2 = "new year";
d2.Print();
//TBase::happy
//TDerived::new year
TDerived<int, string> d3;
d3.data1 = "2020";
d3.data2 = "new year";
d3.Print();
//TBase::2020
//TDerived::new year
return 0;
};