您好,登錄后才能下訂單哦!
這篇文章主要為大家展示了“C++模板編程的示例分析”,內(nèi)容簡而易懂,條理清晰,希望能夠幫助大家解決疑惑,下面讓小編帶領(lǐng)大家一起研究并學(xué)習(xí)一下“C++模板編程的示例分析”這篇文章吧。
在計算機程序設(shè)計領(lǐng)域,為了避免因數(shù)據(jù)類型的不同,而被迫重復(fù)編寫大量相同業(yè)務(wù)邏輯的代碼,人們發(fā)展的泛型及泛型編程技術(shù)。什么是泛型呢?實質(zhì)上就是不使用具體數(shù)據(jù)類型(例如 int、double、float 等),而是使用一種通用類型來進行程序設(shè)計的方法,該方法可以大規(guī)模的減少程序代碼的編寫量,讓程序員可以集中精力用于業(yè)務(wù)邏輯的實現(xiàn)。泛型也是一種數(shù)據(jù)類型,只不過它是一種用來代替所有類型的“通用類型”
我們通常如何實現(xiàn)一個通用的交換函數(shù)呢?
void Swap(int& left, int& right) { int temp = left; left = right; right = temp; } void Swap(double& left, double& right) { double temp = left; left = right; right = temp; } void Swap(char& left, char& right) { char temp = left; left = right; right = temp; } ......
Swap函數(shù)能實現(xiàn)各種類型的變量交換,但是只要類型不同就需要重新寫一個
使用函數(shù)重載雖然可以實現(xiàn),但是有一下幾個不好的地方:
重載的函數(shù)僅僅只是類型不同,代碼的復(fù)用率比較低,只要有新類型出現(xiàn)時,就需要增加對應(yīng)的函數(shù)
代碼的可維護性比較低,一個出錯可能所有的重載均出錯,那能否告訴編譯器一個模版,讓編譯器根據(jù)不同的類型利用該模版來生成代碼呢?
可以的,C++語法中有了模板:
所謂函數(shù)模板,實際上是建立一個通用函數(shù),它所用到的數(shù)據(jù)的類型(包括返回值類型、形參類型、局部變量類型)可以不具體指定,而是用一個虛擬的類型來代替(實際上是用一個標識符來占位),等發(fā)生函數(shù)調(diào)用時再根據(jù)傳入的實參來逆推出真正的類型。 這個通用函數(shù)就稱為 函數(shù)模板(Function Template) 。函數(shù)模板代表了一個函數(shù)家族,該函數(shù)模板與類型無關(guān),在使用時被參數(shù)化,根據(jù)實參類型產(chǎn)生函數(shù)的特定類型版本。
template<typename T1, typename T2,…,typename Tn>
返回值類型 函數(shù)名(參數(shù)列表){}
template<typename T> //或者 template<class T> void Swap(T& x1, T& x2) { T temp = left; left = right; right = temp; }
T1,T2等等是什么類型現(xiàn)在也不確定,一會用的時候才能確定
注意:
typename是用來定義模板參數(shù)關(guān)鍵字,也可以使用class
函數(shù)模板本身并不是函數(shù),是編譯器根據(jù)調(diào)用的參數(shù)類型產(chǎn)生特定具體類型函數(shù)的模具,所以其實模板就是將本來應(yīng)該我們做的重復(fù)的事情交給了編譯器,我們看下面的例子:
template<class T> void Swap(T& x, T& y) { T temp = x; x = y; y = temp; } int main() { int a = 1; int b = 2; Swap(a, b); char A = 'a'; char B = 'b'; Swap(A,B); return 0; }
在編譯器編譯階段,對于模板函數(shù)的使用,編譯器需要根據(jù)傳入的實參類型來推演生成對應(yīng)類型的函數(shù)以供調(diào)用。比如:當用int類型使用函數(shù)模板時,編譯器通過對實參類型的推演,將T確定為int類型,然
后產(chǎn)生一份專門處理int類型的代碼,對于字符類型也是如此。
然而當我們在寫了函數(shù)時,不會進入模板函數(shù)里,沒有寫具體的函數(shù)時,就會進入模板函數(shù)里,我們看下面的例子:
void Swap(int& x, int& y) { int temp = x; x = y; y = temp; } template<class T> void Swap(T& x, T& y) { T temp = x; x = y; y = temp; } int main() { int a = 1; int b = 2; Swap(a, b); char A = 'a'; char B = 'b'; Swap(A,B); return 0; }
我們進行調(diào)式:
我們可以看到int類型的交換函數(shù)我們寫了,調(diào)用時調(diào)用的是我們寫的,而char類型的我們沒寫,就用了模板。
那么這里調(diào)用的是模板函數(shù)嗎?
不是的,實際上這里會有兩個過程
1、模板推演,推演T的具體類型是什么
2、推演出T的具體類型后實例化生成具體的函數(shù)
上面的代碼實例化生成了下面的函數(shù):
void Swap(char& x, char& y) { char temp = x; x = y; y = temp; }
真正調(diào)用的還是兩個函數(shù),但是其中的一個函數(shù)不是我們自己寫的,而是我們給了編譯器一個模板,然后編譯器進行推演在編譯之前實例化生成三個對應(yīng)的函數(shù),模板是給編譯器用的,編譯器充當了寫函數(shù)的工具:
可以看到這里是調(diào)用了Swap<char>函數(shù)
在C++當中,其實內(nèi)置類型也可以像自定義類型那樣這樣初始化:
int a(1); int(2);//匿名
void Swap(T& x1, T& x2) { T temp(x1); x1 = x2; x2 = x1; }
所以模板還可以這樣寫,可以使內(nèi)置類型和自定義類型兼容:
void Swap(T& x1, T& x2) { T temp(x1); x1 = x2; x2 = x1; }
我們來具體看一看函數(shù)模板的實例化:
用不同類型的參數(shù)使用函數(shù)模板時,稱為函數(shù)模板的實例化。模板參數(shù)實例化分為:隱式實例化和顯式實例化。
隱式實例化:讓編譯器根據(jù)實參推演模板參數(shù)的實際類型
template<class T> T Add(const T& left, const T& right) { return left + right; } int main() { int a1 = 10, a2 = 20; double d1 = 10.0, d2 = 20.0; Add(a1, a2); Add(d1, d2); // 此時有兩種處理方式:1. 用戶自己來強制轉(zhuǎn)化 2. 使用顯式實例化 Add(a1, d2); return 0; }
該語句是不能夠通過編譯的,因為在編譯期間,當編譯器看到該實例化時,用a1去推T是int,而用d2去推是double,但是模板參數(shù)列表里只有一個T,編譯器不能明確該T是int還是double,T是不明確的,所以編譯器會報錯
那么怎么處理呢?
解決方式:
1、調(diào)用者自己強制轉(zhuǎn)換
//實參去推演形參的類型 Add(a1, (int)d2); Add((double)a1,d2);
這里可以將d2先強制類型轉(zhuǎn)換,然后再進行推演;或者將a1先強制類型轉(zhuǎn)換再進行推演
2、使用顯式實例化
//實參不需要去推演形參的類型,顯式實例化指定T的類型 Add<int>(a1, d2); Add<double>(a1,d2);
這種方式是顯式實例化指定T的類型
顯式實例化在哪種場景可用呢?看下面的這種場景:
class A { A(int a=0):_a(a) {} private: int _a; }; template<class T> T func(int x) { T a(x); return a; } int main() { func<A>(1); func<int>(2); return 0; }
有些函數(shù)模板里面參數(shù)中沒用模板參數(shù),函數(shù)體內(nèi)才有用到模板參數(shù),此時就無法通過參數(shù)去推演T的類型,這時只能顯示實例化
上面我們提了一點模板參數(shù)的匹配原則,下面我們具體看看模板參數(shù)的匹配原則:
一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,此時如果調(diào)用地方參數(shù)與非模板函數(shù)完全匹配,則會調(diào)用非模板函數(shù)
int Add(int left, int right) { return left + right; } // 通用加法函數(shù) template<class T> T Add(T left, T right) { return left + right; } int main() { Add(1,2);//調(diào)用自己的函數(shù) return 0; }
Add(1,2)參數(shù)是int類型,而我們有現(xiàn)成的int參數(shù)的Add函數(shù),所以有現(xiàn)成的就用現(xiàn)成的,編譯器也會偷懶
那么如果我們想讓這里調(diào)用必須用模板呢?顯式實例化:
Add<int>(1,2);
這樣編譯器就強制會用模板去實例化函數(shù)
一個非模板函數(shù)可以和一個同名的函數(shù)模板同時存在,此時如果調(diào)用地方參數(shù)與非模板函數(shù)不完全匹配,則會優(yōu)先使用模板實例化函數(shù)
int Add(int left, int right) { return left + right; } // 通用加法函數(shù) template<class T> T Add(T left, T right) { return left + right; } int main() { Add(1.1,2.2);//使用模板實例化函數(shù) return 0; }
模板匹配原則總結(jié):
有現(xiàn)成完全匹配的,那就直接調(diào)用,沒有現(xiàn)成調(diào)用的,實例化模板生成,如果有需要轉(zhuǎn)換類型才能匹配的函數(shù)(也就是不完全匹配),那么它會優(yōu)先選擇去實例化模板生成。
優(yōu)先級:
完全匹配>模板>轉(zhuǎn)換類型匹配
template<class T1, class T2, ..., class Tn> class 類模板名 { //類內(nèi)成員定義 };
我們來看一個類模板的使用場景:
typedef int STDateType; class Stack { private: STDateType* _a; int _top; int _capacity; }; int main() { Stack st1; Stack st2; return 0; }
這是我們定義的棧數(shù)據(jù)結(jié)構(gòu),我們創(chuàng)建了兩個棧對象,但是現(xiàn)在st1和st2的存儲數(shù)據(jù)的類型都是int,要是想轉(zhuǎn)換數(shù)據(jù)類型呢?
typedef double STDateType;
我們這樣就轉(zhuǎn)換了,但是我們要是想st1為int,st2為double呢:
Stack st1;//int Stack st2;//double
此時需要寫多個類,名字還得不一樣,如下:
typedef int STDateType1; typedef double STDateType2; class IntStack { private: STDateType1* _a; int _top; int _capacity; }; class DoubleStack { private: STDateType2* _a; int _top; int _capacity; };
這樣太麻煩了,那么什么辦法可以解決呢?類模板可以解決:
//類模板 template<class T> class Stack { private: T* _a; int _top; int _capaticy; }; int main() { //類模板的使用都是顯式實例化 Stack<double> st1; Stack<int> st2; return 0; }
注意:Stack不是具體的類,是編譯器根據(jù)被實例化的類型生成具體類的模具
//類模板 template<class T> class Stack { public: Stack(int capacity = 4) :_a(new T(capacity)) ,_top(0) ,_capacity(capacity) {} ~Stack() { delete[] _a; _a = nullptr; _top = _capacity = 0; } void Push(const T& x) { //... } private: T* _a; int _top; int _capaticy; }; int main() { //類模板的使用都是顯式實例化 Stack<double> st1; Stack<int> st2; return 0; }
注意:類模板的使用都是顯式實例化
假設(shè)我們想類里面聲明和類外面定義成員函數(shù)呢?
//類模板 template<class T> class Stack { public: Stack(int capacity = 4) :_a(new T(capacity)) ,_top(0) ,_capacity(capacity) {} ~Stack() { delete[] _a; _a = nullptr; _top = _capacity = 0; } //假設(shè)我們想類里面聲明和定義分離呢? void Push(const T& x); private: T* _a; int _top; int _capaticy; }; //在類外面定義 template<class T> void Stack<T>::Push(const T& x); { //... } int main() { //類模板的使用都是顯式實例化 Stack<TreeNode*> st1; Stack<int> st2; return 0; }
//在類外面定義 template<class T> void Stack<T>::Push(const T& x); { //... }
在類外面定義我們必須要加模板的關(guān)鍵字,以及需要在實現(xiàn)的函數(shù)前面表明域Stack<T>。普通類,類名就是類型,對于類模板,類名不是類型,類型是Stack<T>,需要寫指定
注意:
模板不支持把聲明寫到.h,定義寫到.cpp,這種聲明和定義分開實現(xiàn)的方式,會出現(xiàn)鏈接錯誤
以上是“C++模板編程的示例分析”這篇文章的所有內(nèi)容,感謝各位的閱讀!相信大家都有了一定的了解,希望分享的內(nèi)容對大家有所幫助,如果還想學(xué)習(xí)更多知識,歡迎關(guān)注億速云行業(yè)資訊頻道!
免責聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進行舉報,并提供相關(guān)證據(jù),一經(jīng)查實,將立刻刪除涉嫌侵權(quán)內(nèi)容。