【 聲明:版權所有,歡迎轉載,請勿用於商業用途。 聯系信箱:feixiaoxing @163.com】
缺省函數是C++的一個基本特色。缺省函數定義比較簡單,也就是說,對於函數的某一個輸入參數或者幾個輸入參數,如果你沒有特定的數值的話,那我們就會用缺省的數據進行代替。如果你在調用的過程中使用了自己的數據,那麼缺省數據將被我們自己定義的數據覆蓋。下面就是一個缺省函數的示例:
int add(int m, int n = 10)
{
return m + n;
}
int add(int m, int n = 10)
{
return m + n;
} 如果調用呢,有什麼區別?
262: int p = add(2);
00401488 push 0Ah
0040148A push 2
0040148C call @ILT+15(add) (00401014)
00401491 add esp,8
00401494 mov dword ptr [ebp-4],eax
263: p = add(3, 4);
00401497 push 4
00401499 push 3
0040149B call @ILT+15(add) (00401014)
004014A0 add esp,8
004014A3 mov dword ptr [ebp-4],eax
262: int p = add(2);
00401488 push 0Ah
0040148A push 2
0040148C call @ILT+15(add) (00401014)
00401491 add esp,8
00401494 mov dword ptr [ebp-4],eax
263: p = add(3, 4);
00401497 push 4
00401499 push 3
0040149B call @ILT+15(add) (00401014)
004014A0 add esp,8
004014A3 mov dword ptr [ebp-4],eax
可以從上面的代碼看到,如果單獨輸入一個數據2,那麼編譯器幫我們默認輸入了10;如果輸了的數據是3、4呢,那麼編譯器將用4代替默認的數據10。所以說,編譯器幫我們做了中間的替換和判斷工作。那麼回到我們今天討論的缺省模板類型上面,那會是什麼樣的情形呢?我們可以編寫一個范例:
template <typename type1, typename type2 = int>
class data
{
type2 value;
public:
data(type2 m): value(m) {}
~data() {}
};
template <typename type1, typename type2 = int>
class data
{
type2 value;
public:
data(type2 m): value(m) {}
~data() {}
};
可以看到,我們在第二個參數使用了缺省類型int,那麼怎麼證明缺省類型可以使用呢?我們設計了下面一個測試用例:
239: data<int, int> m(2);
004013BD push 2
004013BF lea ecx,[ebp-10h]
004013C2 call @ILT+5(data<int,int>::data<int,int>) (0040100a)
004013C7 mov dword ptr [ebp-4],0
240: data<int> n(3);
004013CE push 3
004013D0 lea ecx,[ebp-14h]
004013D3 call @ILT+5(data<int,int>::data<int,int>) (0040100a)
239: data<int, int> m(2);
004013BD push 2
004013BF lea ecx,[ebp-10h]
004013C2 call @ILT+5(data<int,int>::data<int,int>) (0040100a)
004013C7 mov dword ptr [ebp-4],0
240: data<int> n(3);
004013CE push 3
004013D0 lea ecx,[ebp-14h]
004013D3 call @ILT+5(data<int,int>::data<int,int>) (0040100a)
上面的代碼定義了兩個臨時變量,其中第一個是m,輸入類型是int;第二個臨時變量是n,輸入類型是int和int。前面我們說過缺省類型是int,那麼第一個臨時變量m和第二個臨時變量n的構造函數地址應該是一樣的。那麼事實上兩者的構造函數是不是一樣的呢?我們可以查看兩者的函數地址,發現一個是0x0040100a,另外一個也是0x0040100a。范例證明我們的判斷是正確的。
明白了上面的缺省模板構造,下面我們談一下特化模板。特化模板是什麼意思呢?其實並不復雜。因為模板類既然是通用模板,那麼其中的數據類型可以是任意數據類型,但是難免有一些數據類型(比如說指針),我們需要對其中的一些操作做一些細微的修改,但是這些小的修改在原來的模板定義上是無法做的。那麼怎麼辦?我們只好重新定義一種形式,它和模板類定義的名稱一致,但是形式稍有差別。我們可以編寫一個測試看看:
template <typename type>
class data
{
public:
data() {printf("normal!\n");}
~data() {printf("~normal!\n");}
};
template <>
class data<int*>
{
public:
data() {printf("point!\n");}
~data() {printf("point!\n");}
};
template <typename type>
class data
{
public:
data() {printf("normal!\n");}
~data() {printf("~normal!\n");}
};
template <>
class data<int*>
{
public:
data() {printf("point!\n");}
~data() {printf("point!\n");}
};
上面的代碼定義了兩個類模板。但是兩者的名稱是一樣的,說明這兩個類定義的內容其實具有很大的相似性。第一種定義就是標准模板類的定義,第二中稍微復雜一點,使用缺省的int*,因為沒有使用到特定的type類型,所以此時template後面的內容為空。那麼怎麼判斷這兩個類都是可以正常使用的呢?大家可以看看下面的范例:
249: data<int> p;
004013BD lea ecx,[ebp-10h]
004013C0 call @ILT+45(data<int>::data<int>) (00401032)
004013C5 mov dword ptr [ebp-4],0
250: data<int*> q;
004013CC lea ecx,[ebp-14h]
004013CF call @ILT+35(data<int *>::data<int *>) (00401028)
251: }
249: data<int> p;
004013BD lea ecx,[ebp-10h]
004013C0 call @ILT+45(data<int>::data<int>) (00401032)
004013C5 mov dword ptr [ebp-4],0
250: data<int*> q;
004013CC lea ecx,[ebp-14h]
004013CF call @ILT+35(data<int *>::data<int *>) (00401028)
251: }
我們發現,第一個函數的call地址是0x00401032,第二個地址為0x00401028。但是這說明不了什麼,因為第二個地址完全也可能是第一個模板類引申的。我們應該跟到每一個函數裡面(其實這裡的地址在VC下都是跳轉地址)。
第一個變量的實際進入函數如下所示:
234: data() {printf("normal!\n");}
00401340 push ebp
00401341 mov ebp,esp
00401343 sub esp,44h
00401346 push ebx
00401347 push esi
00401348 push edi
00401349 push ecx
0040134A lea edi,[ebp-44h]
0040134D mov ecx,11h
00401352 mov eax,0CCCCCCCCh
00401357 rep stos dword ptr [edi]
00401359 pop ecx
0040135A mov dword ptr [ebp-4],ecx
0040135D push offset string "normal!\n" (0042607c)
00401362 call printf (00401540)
00401367 add esp,4
0040136A mov eax,dword ptr [ebp-4]
0040136D pop edi
0040136E pop esi
0040136F pop ebx
00401370 add esp,44h
00401373 cmp ebp,esp
00401375 call __chkesp (004023b0)
0040137A mov esp,ebp
0040137C pop ebp
0040137D ret
234: data() {printf("normal!\n");}
00401340 push ebp
00401341 mov ebp,esp
00401343 sub esp,44h
00401346 push ebx
00401347 push esi
00401348 push edi
00401349 push ecx
0040134A lea edi,[ebp-44h]
0040134D mov ecx,11h
00401352 mov eax,0CCCCCCCCh
00401357 rep stos dword ptr [edi]
00401359 pop ecx
0040135A mov dword ptr [ebp-4],ecx
0040135D push offset string "normal!\n" (0042607c)
00401362 call printf (00401540)
00401367 add esp,4
0040136A mov eax,dword ptr [ebp-4]
0040136D pop edi
0040136E pop esi
0040136F pop ebx
00401370 add esp,44h
00401373 cmp ebp,esp
00401375 call __chkesp (004023b0)
0040137A mov esp,ebp
0040137C pop ebp
0040137D ret
那麼,第二個變量呢,同樣需要跟入函數:
242: data() {printf("point!\n");}
00401430 push ebp
00401431 mov ebp,esp
00401433 sub esp,44h
00401436 push ebx
00401437 push esi
00401438 push edi
00401439 push ecx
0040143A lea edi,[ebp-44h]
0040143D mov ecx,11h
00401442 mov eax,0CCCCCCCCh
00401447 rep stos dword ptr [edi]
00401449 pop ecx
0040144A mov dword ptr [ebp-4],ecx
0040144D push offset string "point!\n" (00426074)
00401452 call printf (00401540)
00401457 add esp,4
0040145A mov eax,dword ptr [ebp-4]
0040145D pop edi
0040145E pop esi
0040145F pop ebx
00401460 add esp,44h
00401463 cmp ebp,esp
00401465 call __chkesp (004023b0)
0040146A mov esp,ebp
0040146C pop ebp
0040146D ret
242: data() {printf("point!\n");}
00401430 push ebp
00401431 mov ebp,esp
00401433 sub esp,44h
00401436 push ebx
00401437 push esi
00401438 push edi
00401439 push ecx
0040143A lea edi,[ebp-44h]
0040143D mov ecx,11h
00401442 mov eax,0CCCCCCCCh
00401447 rep stos dword ptr [edi]
00401449 pop ecx
0040144A mov dword ptr [ebp-4],ecx
0040144D push offset string "point!\n" (00426074)
00401452 call printf (00401540)
00401457 add esp,4
0040145A mov eax,dword ptr [ebp-4]
0040145D pop edi
0040145E pop esi
0040145F pop ebx
00401460 add esp,44h
00401463 cmp ebp,esp
00401465 call __chkesp (004023b0)
0040146A mov esp,ebp
0040146C pop ebp
0040146D ret
看到上面的函數,大家應該明白了兩者調用的構造函數並不一樣。所以說,特化模板通常就是為了那些特殊的數據類型准備的。這樣我們使用者在使用模板的時候就沒有什麼顧慮了,可以忽略各個數據類型處理上的差別了。當然,特化模板因為考慮了特殊模型數據,使得我們的代碼更加完畢,更加健壯了,建議在設計模板的時候適當多使用。
思考題:
(1)模板類第一個type可以缺省嗎?為什麼會這麼考慮?
(2)下面的代碼在vc 6.0和vc 2005上都能編譯過?為什麼呢?對於我們設計代碼有什麼思考呢? (建議從兼容性上面考慮)
template <typename type>
class data
{
public:
data() {printf("normal!\n");}
~data() {printf("~normal!\n");}
};
template <typename type>
class data <type*>
{
public:
data() {printf("point!\n");}
~data() {printf("point!\n");}
};
template <typename type>
class data
{
public:
data() {printf("normal!\n");}
~data() {printf("~normal!\n");}
};
template <typename type>
class data <type*>
{
public:
data() {printf("point!\n");}
~data() {printf("point!\n");}
};