QQ:2279557541
Email:[email protected]
博客地址:http://blog.csdn.net/lihn1987
優酷主頁:http://i.youku.com/shuangzhixiaodao
首先,我們爲什麼要使用模板?
比如我們幾乎所有的C++招聘信息中都會要求的“熟練使用STL”,STL是什麼意思呢? 其實“STL”就是“Standard Template Library”的縮寫,翻譯過來也就是“標準模板庫”的意思,其中就運用到了大量的模板操作。
由於C++是一個強類型語言,而我們有許多代碼除了類型以外其他的實現幾乎完全一樣。
1. 模板函數的編寫
我們來演示一個例子。
求兩個數中的最大值。
很多公司的筆試題中都有一道題,實現max(a,b)這個宏。那這裏我們就來用模板實現一下。
template<typename T>
const T max(const T& t1, const T& t2)
{
return (t1 >t2)?t1:t2;
}
這個函數的目的是求兩個值t1和t2中的最大值。而類型是由T來確定的。只要在函數前面聲明一個template<typename 類型1,typename 類型2...typename 類型n>。那麼其後面的函數就會把聲明的參數當做一個類型來處理。這裏說明一下,typename由於歷史原因其實是可以用class來代替的。
2. 模板函數的使用
#include "stdafx.h"
#include <iostream>
#include <string>
template<typename T>
const T max(const T& t1, const T& t2)
{
return (t1 >t2)?t1:t2;
}
int main()
{
{
int t1 = 10,t2 = 20;
std::cout << max<int>(t1,t2) << std::endl;
}
{
double t1 = 10.1,t2 = 20.2;
std::cout << max<double>(t1,t2) << std::endl;
}
{
char t1 ='a', t2 ='b';
std::cout << max<char>(t1,t2) << std::endl;
}
{
std::string t1 ="aaa", t2 = "bbb";
std::cout << max<std::string>(t1,t2) << std::endl;
}
system("pause");
}
輸出爲
20
20.2
b
Bbb
看這一段代碼,模板函數的標準使用方式是
函數名稱<模板參數1,模板參數2...>(函數參數...)
3. 模板的實現原理
看完上一段代碼,我們來解釋一下模板的實現原理。
比如我們調用了max<int>(t1,t2)其實是將int類型帶入到了模板中,然後生成了一個函數,樣子爲
const int max(const int& t1, const int& t2)
{
return (t1 >t2)?t1:t2;
}
根據你調用類型不同生成不同的函數。而這幾個函數其實是不相同的,我們看下反彙編的結果。
代碼爲
#include "stdafx.h"
#include <iostream>
#include <string>
template<typename T>
const T max(const T& t1, const T& t2)
{
return (t1 >t2)?t1:t2;
}
int main()
{
int i1 = 1,i2 = 2;
double d1 = 1.0,d2 = 2.0;
std::string s1 ="1", s2 ="2";
max<int>(i1,i2);
max<double>(d1,d2);
max<std::string>(s1,s2);
system("pause");
}
反彙編的結果爲
可以看到調用的max的函數地址,一個是0181393h一個是01814bah,是不同的函數地址。
4. 模板函數的推導
template<typename T>
const T max(const T& t1, const T& t2)
{
return (t1 >t2)?t1:t2;
}
模板函數在調用時時需要確定模板參數的,模板參數有兩種方式指定
一種爲顯示的指定,如
max<int>(1, 2);
一種爲隱形的指定,如
max(1,2);
這裏的推導需要說明一下,由於模板函數的參數是T類型的,而模板參數也是T類型,則隱形的將這裏傳入的參數是int類型的,這裏就推導T的類型爲int型了。
因此在使用模板函數的時候,只要參數能夠自動推導正確,我們是不需要進行顯示指定模板參數類型的。
5. 模板函數的重載和特化
#include <iostream>
#include <string>
const int max(int t1,int t2)
{
std::cout << "這是非模板函數"<<std::endl;
return (t1 >t2) ? t1 :t2;
}
template<typename T>
const T max(const T& t1,const T& t2)
{
std::cout << "這是泛模板函數" << std::endl;
return (t1 >t2) ? t1 :t2;
}
int main()
{
int i1 = 1,i2 = 2;
max(i1,i2);
max<>(i1,i2);
system("pause");
}
輸出爲
這是非模板函數
這是泛模板函數
這裏對max的實現有兩種。一種是普通的max函數,參數爲int類型。另一種是模板類型。其實是對max進行了重載。而在重載的時候有個順序,就是優先判斷普通函數是否能夠處理這個參數的函數,如果能的話,則優先調用。
我們再看一下函數的另一種“重載”方式,就是模板的特化。
#include "stdafx.h"
#include <iostream>
template<typename T>
const T max(const T& t1,const T& t2)
{
std::cout << "這是泛模板函數" << std::endl;
return (t1 >t2) ? t1 :t2;
}
template<>
const int max(const int& t1,const int& t2)
{
std::cout << "這是特化模板函數" << std::endl;
return (t1 >t2) ? t1 :t2;
}
int main()
{
int i1 = 1,i2 = 2;
double d1 = 1,d2 = 2;
max(i1,i2);
max(d1,d2);
system("pause");
}
輸出爲
這是特化模板函數
這是泛模板函數
首先說下特化的語法,其實就是在函數前面加一個template<>,然後將本來不明確的類型,進行明確的定義,也就是對原來模板函數對某種特定的類型進行特殊的處理。
其實這裏的特化其實和剛纔的重載沒有什麼太大的區別,貌似都是和模板函數的重載一樣是對函數某個類型的參數特殊處理。
然後,調用規則同重載一樣,優先判斷模板的特化函數有沒有能夠處理這種這種,如果有,則優先調用特化,否則則都要用原來的模板函數。
6. 特化真的這麼簡單?
#include "stdafx.h"
#include <iostream>
template<typename T>
void func(T*)
{
std::cout << "func T*";
}
template<typename T>
void func(T)
{
std::cout << "func T";
}
int main()
{
char* tmp = NULL;
func(tmp);
system("pause");
}
這會調用哪個函數呢??????
如果你嘗試着運行一下。。。
沒錯!就是void func(T*)
這裏可能有些迷惑~我們發現我們傳入一個char*的參數,兩個模板都能夠匹配。
分別爲func<char>(char*)和func<char*>(char*)兩種,而爲何選擇了第1種呢?有些書上有種說法是:選擇更加特化的,或者叫更加特殊的。我看了以後一頭霧水。
我是這樣理解的,推導中模板匹配的越簡單的也就是上面推倒的尖括號中的部分也簡單的,越線被匹配到。
7. 模板函數的重載和特化同時存在
void func(T)
{
std::cout << "func T" << std::endl;
}
template<>
void func(int)
{
std::cout << "func T*"<<std::endl;
}
void func(int)
{
std::cout << "func" << std::endl;
}
int main()
{
int tmp = 0;
func(tmp);
system("pause");
}
這種情況比較特殊,名爲func的普通函數,模板函數,模板特化函數都存在的情況,其是會報錯和,還是有優先順序呢?
實際情況是,這種方式是允許的,其匹配優先級爲 普通函數>特化函數>原始模板函數