24.在函數重載和設定參數缺省值間要慎重選擇。
獲得一種類型的數據的最小值或最大值,對于c中,一般使用在<linits.h>中定義的各種宏如INT_MIN 來進行表示,但是這樣無法進行泛型編程,即對應如何一種類型T返回對應類型的最小或最大值。而在c++中一般如此獲得
~~~
std::numeric_limits<T>::min()
~~~
c++在<limits>中定義了類模版numeric_limits,用來返回對應類型的最小最大值,這是一個很有用的東西。
然后繼續討論函數重載與參數缺省值,如以下情況:
~~~
int fun(){
return 1;
}
int fun(int a){
return a;
}
int fun(int a=1,int b = 0){
return a+b;
}
~~~
對于第3個函數,當沒有參數和只有一個參數時會與前兩個函數沖突,但是對于第三個函數,即有默認值的情況下,其能直接具有全部三個函數的功能,使用默認值的函數其效果更好且功能更多。
但是有時找不到一個好的缺省值。當對5個以內的值求和時,可以設每個參數的默認值為0,但是當對5個以內的值進行求平均數時,要獲得傳入參數的個數,無法通過函數的參數來實現,所以只能重載5個函數,即只有一個,兩個,3,4,5個函數的所有情況。
另一種必須使用重載函數的情況是:想完成一項特殊的任務,但算法取決于給定的輸入值。就是說函數由于輸入參數不同進行操作不同的這類函數要重載,如類的構造函數。
25.避免對指針和數字類型的重載。
如函數 void f(int x);和void f(int * p);這兩個函數的,重載是會出錯,簡單來說對于實參為0,即0是什么?0即是指針有是int,但事實上在編譯器中運行
~~~
void f(int* x){
cout<<"int_ptr"<<endl;
}
void f(int x){
cout<<"int"<<endl;
}
int main(){
f(NULL);
~~~
結果為 輸出 int ,即0是一個int。人們認為一個調用具有多義性時,編譯器卻不這么干。
NULL的類型是就是int,要使其調用 f(int*)這個函數,就必須如此做
~~~
f(static_cast<int*>(NULL));
~~~
但是如果將NULL定義為UL,即無符號整數
~~~
#define NULL 0UL
~~~
在調用f(NULL)又會報錯,重載不明確,NULL即可以是int也可以是int*
但是如果又將f(int)函數改為
~~~
void f(unsigned long x);
~~~
又會正確,因為NULL是unsigned long,而f函數中參數也是。
而如果我們需要一個任何地方都可以使用的任意類型的NULL指針,就必須設計一個產生NULL指針對象的類:
~~~
#ifdef NULL
#undef NULL
#endif
class NullClass{
public:
template<class T> //模版
operator T*()const{return 0;}//返回一個T*的null指針。
};
const NullClass NULL;//NULL的常量
~~~
然后再次使用 f(NULL)的時候,就會調用隱式的類型轉換,獲得一個對象T類型的null指針。但是這樣還不夠,改進:
首先我們只需要一個NullClass對象,所以給這個類一個名字是沒有必要的,定義一個匿名類并使NULL成為這種類型。
其次,我們想讓NULL可以轉換為如何類型的指針,那就要能夠處理成員指針(指 指向類中函數的指針),要再定義一個成員模版,將類C與所有類型T轉換為類型 T C::*。
最后要防止用戶取NULL的地址,NULL要表現的像指針一樣,但其值不是指向真正的0,所以要對用戶隱藏。
改進后如下:
~~~
class {
public:
template<class T> //模版
operator T*()const{return 0;}//返回一個T*的null指針。
template<class C,class T>
operator T C::*() const{return 0;}//轉換任意類型的null成員指針
private:
void operator&() const ;//隱藏NULL的地址
}NULL; //只有一個名字為NULL的對象
~~~