目錄
- 前言
- 1.泛型編程
- 2.函式模板
- 2.1函式模板概念
- 2.2 函式模板格式
- 2.3 利用模板來實作兩個數的交換
- 2.3 函式模板原理
- 2.4 函式模板的實體化
- 2.4.1 隱式實體化
- 2.4.2 顯式實體化
- 2.5 模板型別匹配原則(擇優)
- 3. 類模板
- 3.1 類模板定義的格式
- 3.2 類模板的一個應用實體
- 3.3 類模板實體化
- 4.模板不支持分離編譯
- 5.后記
前言
hello,大家好,今天博主來繼續更新C++系列的文章,今天我們來分享的是模板,歡迎大家繼續支持,
1.泛型編程
假如我們需要實作兩個數的交換,但因為這兩個數字有可能是int型,也有可能是double型,還有可能是char型,所以,為了每一種型別的數字都可以交換,我們就要針對每一種型別都寫一個函式,這樣做很麻煩,在C++中,是支持多載技術的,這樣我們可以把每一種型別的交換函式都命名為Swap,但是,當有新的型別出現時,依舊需要增加新的函式,使用起來依舊不方便,
下面的代碼展現了針對不同型別的交換函式,
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;
}
......
既然如此麻煩,我們有沒有辦法能夠使這種情況更簡潔一些呢?能否告訴編譯器一個模子,讓編譯器根據不同的型別利用該模子來生成代碼呢?這樣我們就可以從寫許多函式中解放出來,
于是我們引入了泛型編程,即撰寫與型別無關的通用代碼,是代碼復用的一種手段,模板是泛型編程的基礎,
模板分為函式模板和類模板,接下來我們就來介紹他們,
2.函式模板
2.1函式模板概念
函式模板代表了一個函式家族,該函式模板與型別無關,在使用時被引數化,根據實參型別產生函式的特定型別版本,
2.2 函式模板格式
>template<typename T1, typename T2,......,typename Tn>
回傳值型別 函式名(引數串列)
{
……
}
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
typename是用來定義模板引數關鍵字,也可以使用class(切記:不能使用struct代替class)
2.3 利用模板來實作兩個數的交換
#include<iostream>
using namespace std;
template<typename T>
void Swap(T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
int main()
{
int a = 1;
int b = 2;
Swap(a, b);
cout << "a:" << a << "," << "b:" << b << endl;
double c = 3;
double d = 4;
Swap(c, d);
cout << "c:" << c << "," << "d:" << d << endl;
return 0;
}

我們看,利用模板是不是方便許多呢?
那么你心中是否有一個疑問,當我們在使用模板的時候,我們呼叫的是同一個函式嗎?我們來看一下匯編代碼:

我們發現兩次呼叫的函式地址是不同的,這說明他們不是一個函式,
2.3 函式模板原理
既然我們知道,即使有了模板,編譯器在處理的時候,依舊是按照不同函式來處理的,那么編譯器處理的原理又是什么呢?
函式模板是一個藍圖,它本身并不是函式,是編譯器用使用方式產生特定具體型別函式的模具,所以其實模板就是將本來應該我們做的重復的事情交給了編譯器,
在編譯器編譯階段,對于模板函式的使用,編譯器需要根據傳入的實參型別來推演生成對應型別的函式以供呼叫,比如:當用double型別使用函式模板時,編譯器通過對實參型別的推演,將T確定為double型別,然后產生一份專門處理double型別的代碼,對于字符型別也是如此,

也就是說,所謂的函式模板,就是把本來需要我們來寫的代碼交給編譯器來寫,所以,模板不只是Ctrl+V,而是根據實際情況,處理成不同的代碼,這其實就是一種甩鍋啊,本來應該我們做的事情,由于我們的“懶惰”,我們交給了編譯器去做,然后編譯器默默承擔了所有,

2.4 函式模板的實體化
用不同型別的引數使用函式模板時,稱為函式模板的實體化,模板引數實體化分為:隱式實體化和顯式實體化,
所謂的模板實體化就是將模板運用到具體情境中,將一個抽象的化為具體的,交換兩個數很模糊,交換兩個int型對數就清晰很多,
函式模板的實體化分為隱式實體化和顯式實體化,接下來我們就來介紹他們,
2.4.1 隱式實體化
讓編譯器根據實參推演模板引數的實際型別
#include<iostream>
using namespace std;
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);
return 0;
}

但是,如果我們將上面的代碼添加一行a1+d2的代碼,編譯器就會報錯,

報錯的原因很簡單,因為a1和d2的型別不同,編譯器懵了,不知道該咋整了,于是這個時候,我們就需要明確地告訴編譯器按照上面型別來相加,于是我們就需要顯式實體化,
2.4.2 顯式實體化
在顯式實體化中,我們需要用<>來告訴編譯器,我們期待它處理成什么型別的資料,我們將上面的代碼用<>來點綴一下,
#include<iostream>
using namespace std;
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);
Add<int>(a1, d2);
return 0;
}

2.5 模板型別匹配原則(擇優)
- 一個非模板函式可以和一個同名的函式模板同時存在,而且該函式模板還可以被實體化為這個非模板函式
#include<iostream>
using namespace std;
// 專門處理int的加法函式
int Add(int left, int right)
{
return left + right;
}
// 通用加法函式
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 與非模板函式匹配,編譯器不需要特化
Add<int>(1, 2); // 呼叫編譯器特化的Add版本
}
int main()
{
Test();
}
我們看,在這段代碼中,既有普通定義的Add函式,又有模板的Add函式,二者是可以同名的,那么我們不禁要問,在不同情況下,編譯器會呼叫普通函式還是模板呢?
我們看Add(1,2)這個呼叫陳述句就是普通呼叫陳述句,編譯器會直接呼叫Add函式,對于編譯器來說,有現成的函式不去呼叫,干嘛要在去自己才該生成哪一種型別的函式啊?
而對于Add(1,2),陳述句已經告訴編譯器顯式實體化了,證明是要模板的,所以這個呼叫的是模板函式,

2. 對于非模板函式和同名函式模板,如果其他條件都相同,在調動時會優先呼叫非模板函式而不會從該模板產生出一個實體,如果模板可以產生一個具有更好匹配的函式, 那么將選擇模板,
// 專門處理int的加法函式
int Add(int left, int right)
{
return left + right;
}
// 通用加法函式
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2); // 與非函式模板型別完全匹配,不需要函式模板實體化
Add(1, 2.0); // 模板函式可以生成更加匹配的版本,編譯器根據實參生成更加匹配的Add函式
}
在這個例子中,Add(1,2.0)是呼叫模板的,因為模板可以根據實參生成匹配的版本,而普通的Add函式卻不可以滿足double型,所以編譯器會選擇模板,

3. 模板函式不允許自動型別轉換,但普通函式可以進行自動型別轉換
如果模板支持自動型別轉換的話,還要<>干什么?

3. 類模板
3.1 類模板定義的格式
template<class T1, class T2, ..., class Tn>
class 類模板名
{
// 類內成員定義
};
3.2 類模板的一個應用實體
// 動態順序表
// 注意:Vector不是具體的類,是編譯器根據被實體化的型別生成具體類的模具
template<class T>
class Vector
{
public :
Vector(size_t capacity = 10)
: _pData(new T[capacity])
, _size(0)
, _capacity(capacity)
{}
// 使用解構式演示:在類中宣告,在類外定義,
~Vector();
void PushBack(const T& data);
void PopBack();
// ...
size_t Size() {return _size;}
T& operator[](size_t pos)
{
assert(pos < _size);
return _pData[pos];
}
private:
T* _pData;
size_t _size;
size_t _capacity;
};
// 注意:類模板中函式放在類外進行定義時,需要加模板引數串列
template <class T>
Vector<T>::~Vector()
{
if(_pData)
delete[] _pData;
_size = _capacity = 0;
}
3.3 類模板實體化
類模板實體化與函式模板實體化不同,類模板實體化需要在類模板名字后跟<>,然后將實體化的型別放在<>中即可,類模板名字不是真正的類,而實體化的結果才是真正的類,
// Vector類名,Vector<int>才是型別
Vector<int> s1;
Vector<double> s2;
4.模板不支持分離編譯
模板不支持分離編譯,也就是說模板不支持宣告在.h,定義在.cpp,
建議定義在一個檔案里,
5.后記
好的,今天我們關于模板的知識就先介紹到這里,希望對大家有所幫助,我們在文章中已經介紹過,模板的原理其實就是甩鍋,我想起了小時候讀過的一個很有趣的甩鍋的故事,北宋書畫家米芾,在一個地方做縣令,有一年鬧起了蝗災,米芾帶領百姓消滅了蝗災,不料隔壁縣也爆發了蝗災,隔壁縣令苦惱不已,于是就想甩鍋給米芾,說自己縣里的蝗蟲都是米芾的縣趕去的,米芾收到甩鍋信看完后當然不答應,于是回了他一首打油詩,很有趣,分享如下:
蝗蟲本是天災,豈由小官遣派?
倘說鄙人趕去,速請閣下押來,
轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/295453.html
標籤:其他
上一篇:LeetCode通關:堆疊和佇列六連,匹配問題有絕招
下一篇:值得關注的HTML基礎
