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的对象
~~~