【 聲明:著作權,歡迎轉載,請勿用於商業用途。 聯絡信箱:feixiaoxing @163.com】
如果類是一種確定的資料類型,那麼模板就是一種對類的抽象。假設有這麼一種類,它需要進行資料的計算,而且類型還很多,那麼我們可能就要針對不同類型的資料定義不同的類。我們可以用下面一段代碼說明問題:
class int_process{int a;int b;public:int_process(int m, int n):a(m), b(n) {}~int_process() {}int add() {return a + b;}int sub() {return a - b;}int mul() {return a * b;}int div() {return a / b;}};class short_process{short a;short b;public:short_process(short m, short n):a(m), b(n) {}~short_process() {}short add() {return a + b;}short sub() {return a - b;}short mul() {return a * b;}short div() {return a / b;}};
上面的代碼內容其實比較簡單,大家可以看明白。第一個類是int_process,主要是整數的加、減、乘、除的計算。第二類是short_process,主要處理的短整數的加、減、乘、除計算。兩個類處理的內容其實非常相似。那麼有沒有一種簡單的辦法可以同時處理這兩個類?有!這就是模板。我們可以把具體的資料類型抽象出來,形成一種新的類模式。這就是模板類。下面的代碼就是模板類:
template <typename type>class data_process{type a;type b;public:data_process(type m, type n):a(m), b(n) {}~data_process() {}type add() {return a + b;}type sub() {return a - b;}type mul() {return a * b;}type div() {return a / b;}};
我們看到類把具體的資料類型都抽象成了type。至此,不管是輸入值、輸出數值,我們都換成了type。至於類的名稱,我們也從原來特定的資料類型計算,轉變成了通用的data_process,當然這種名稱的定義不是太重要的。那麼模板類定義之後,我們應該怎麼應用呢?大家繼續看代碼:
void process(){data_process<int> d(1,2);data_process<char> m('1', '2');data_process<double> p(1.2, 2.3);}
大家從上面的代碼也看的出,模板類的定義並不複雜,只是在模板類的名稱之後添加一下具體的資料類型就可以了。如果是int類型的,那麼處理int的資料;同理,如果處理的是char或者是double類型資料,我們就可以按照char或者是double類型的資料進行計算,十分方便。當談,處理的資料遠遠不止C++語言本身定義的char、double、float、int、short、long這幾種資料類型,如果type本身就是一種class類型,同時這樣class類型也支援+、-、*、/運算,那麼本身也是可以用作模板的。我們這裡介紹int、char、double只是為了簡單地說明問題。看到類的聲明後,我們不禁有一個疑問,既然模板類只有一個,那麼這些模板類的建構函式、解構函式、成員函數的處理都相同嗎?我們不妨看看看一看他們的彙編代碼:
60: data_process<int> d(1,2);0040126D push 20040126F push 100401271 lea ecx,[ebp-14h]00401274 call @ILT+45(data_process<int>::data_process<int>) (00401032)00401279 mov dword ptr [ebp-4],061: data_process<char> m('1', '2');00401280 push 32h00401282 push 31h00401284 lea ecx,[ebp-18h]00401287 call @ILT+55(data_process<char>::data_process<char>) (0040103c)0040128C mov byte ptr [ebp-4],162: data_process<double> p(1.2, 2.3);00401290 push 40026666h00401295 push 66666666h0040129A push 3FF33333h0040129F push 33333333h004012A4 lea ecx,[ebp-28h]004012A7 call @ILT+60(data_process<double>::data_process<double>) (00401041)004012AC mov byte ptr [ebp-4],263: int i_d = d.add();004012B0 lea ecx,[ebp-14h]004012B3 call @ILT+70(data_process<int>::add) (0040104b)004012B8 mov dword ptr [ebp-2Ch],eax64: char c_m = m.add();004012BB lea ecx,[ebp-18h]004012BE call @ILT+80(data_process<char>::add) (00401055)004012C3 mov byte ptr [ebp-30h],al65: double d_p = p.add();004012C6 lea ecx,[ebp-28h]004012C9 call @ILT+75(data_process<double>::add) (00401050)004012CE fstp qword ptr [ebp-38h]66:67: } 上面的代碼有點長,我們大家來一起看一下: 60句: 定義int型的class類型,可以看到data_process<int>建構函式地址是0x401032 61句: 定義char型的class類型,看到data_process<char>建構函式地址是0x40103c 62句:定義double型的class類型,看到data_process<double>建構函式地址是0x401041 63句:調用data_process<int>的add成員函數,地址為0x40104b 64句:調用data_process<char>的add成員函數,地址為0x401055 65句:調用data_process<double>的add成員函數,地址為0x401050 上面的代碼錶明,其實編譯器為我們函數中出現的每一個具體類執行個體化了一遍。針對每一個類型,模板的建構函式、解構函式、成員函數都要獨立產生,這從上面的函數地址就可以看出來,沒有什麼神奇的。所以,我們明白了模板的本質就是對不同資料類型的相似性操作進行共同屬性提取,合成模板。在應用的時候,編譯器根據我們使用中的資料類型獨立產生每一個類,構建每一個基本運算變數和運算函數,僅此而已。
模板注意事項: (1)class上出現的問題在模板類上都會出現 (2)先把class寫好,然後再轉變成模板類 (3)如果不是資料類型的差異,而是共有資料數量上的差異,請選用繼承代替模板 (4)模板中的type可以是自訂類型 (5)模板代碼只能出現在標頭檔中,出現在*.cpp檔案中沒有意義,單獨的*.cpp模板代碼因為沒有涉及具體類型,因此不會編譯成任何二進位代碼 (6)不同版本的vc對模板支援有差異,編譯錯誤不一定是你自己的原因,但是絕大部分應該是你的原因 (7)模板產生的警示很冗長,一個warning或者是error 30~50行很正常,不要害怕,孰能生巧
【預告: 下面的部落格介紹模板函數】