首先,函数为什么能够重载?
因为我们在编译和链接的过程中,通过名字粉碎技术,给同名函数起了不同的名字,新的名字要把它的类型、参数作为它们名称的一部分,在源码上我们看到的是相同的函数名,但是在编译和链接的过程中,由于我们把参数列表作为函数名的一部分,那么函数名实际上是不一样的,可以进行区分。
1.C语言实现int,double,char类型的比较大小函数
int my_max_i(int a,int b){return a>b?a:b;}
double my_max_d(double a,double b){return a>b?a:b;}
char my_max_c(char a,char b){return a>b?a:b;}
这些函数都执行了相同的一般性动作;都返回两个形参中的最大值;从用户的角度来看, 只有一种操作,就是判断最大值,至于怎样完成其细节, 函数的用户一点也不关心。
这种词汇上的复杂性不是”判断参数中的最大值“问题本身固有的,而是反映了程序设计环境的一 种局限性:在同一个域中出现的名字必须指向一 个唯实体(函数体)。
这种复杂性给程序员带来了一个实际问题,他们必须记住或查找每一个函数名字。
函数重载把程序员从这种词汇复杂性中解放出来。
2.函数重载的概念:
在C++中可以为两个或两个以上的函数提供相同的函数名称,只要参数类型不同,或参数类型相同而参数的个数不同,称为函数重载。
//my_max + 参数表
int my_max(int a,int b)
{
return a>b?a:b;
}
double my_max(double a,double b)
{
return a>b?a:b;
}
char my_max(char a,char b)
{
return a>b?a:b;
}
//每个同名函数的参数表是唯一的
int main()
{
int ix=my_max(12,23);
double dx=my_max(12.23,34,45);
char chx=my_max('a','b');
return 0;
}
编译器的工作:
当一个函数名在同一个域中被声明多次时,编译器按如下步骤解释第二个(以及后续的)声明。
如果两个函数的参数表中的参数个数或类型或顺序不同,则认为这两个函数是重载。
例如:
//重载函数,这种力求避免
void print(int a,char b)
void print(char a,int b)`
3.判断函数重载的规则:
1)如果两个函数的参数表相同,但是返回类型不同,会被标记为编译错误:函数的重复声明。
int my_max(int a,int b)
{
return a>b?a:b;
}
unsigned int my_max(int a,int b)//error
{
return a>b?a:b;
}
int main()
{
int ix=my_max(12,23);
unsigned int ui=my_max(12,23);//error
return 0;
}
2)参数表的比较过程与形参名无关,只比较形参的类型、个数以及顺序
//声明同一个函数
int my_add(int a,iny b);
int my_add(int x,int y);
3)如果在两个函数的参数表中,只有缺省实参不同,则第二个声明被视为第一个的重复声明。
void Print(int *br,int n);
void Print(int *br,int len=10);
4)typedef名为现有的数据提供了一个替换名,它没有创建一个新类型,因此,如果两个函数的参数表区别只在于一个使用了typedef,而另一个使用了与typedef相应的类型,则该参数表被视为相同的参数表。
typedef unsigned int u_int;
int Print(u_int a);
int Print(unsigned int b);
5)当一个形参类型有const或volatile修饰时,如果形参是按值传递方式定义,在识别函数声明是否相同时,并不考虑const和volatile修饰符。
void fun(int a){}
void fun(const int a){}
6)当一个形参类型有const或volatile修饰时,如果形参定义指针或者引用时,在识别函数声明是否相同时,就需要考虑const和volatile修饰符。
void fun(int *p){}
void fun(const int *p){}
void func(int &a){}
void func(const int &a){}
int main()
{
int a=10;
const int b=20;
fun(&a);//void fun(int *p)
fun(&b);//void fun(const int *p)
int x=10;
func(x);//void func(int &a)
func(10);//void func(const int &a)
}
7)注意函数调用的二义性:
如果在两个函数的参数表中,形参类型相同,而形参个数不同,形参默认值将会影响函数的重载。
//ok
void fun(int a){}
void fun(int a,int b){}
int main()
{
fun(12);//void fun(int a)
fun(12,23);//void fun(int a,int b)
}
//error 二义性
void fun(int a){}
void fun(int a,int b=10){}//实参可以给一个也可以给两个
int main()
{
fun(12);//error,出现二义性
fun(12,23);
}
8)函数重载解析的步骤如下
(1)确定函数调用考虑的重载函数的集合,确定函数调用中实参表的属性;
(2)从重载函数集合中选择函数,该函数可以在(给出实参个数和类型)的情况下可以调用函数;
(3)选择与调用最匹配的函数。
void print(int a){}
void print(int &b){}
void print(const int &c){}
int main()
{
int x=10;
const int y=20;
print(x);//最佳匹配:print(int &b)//如果没有普通引用 print(const int &c)
//void print(int a){}
//void print(int &b){}
//print(x)以上两个均可调用,因此当两者同时出现时会出现二义性,屏蔽第一个函数,编译能通过
print(y);//最佳匹配:print(const int &c),如果没有常引用函数其它函数均不能与其匹配
//屏蔽第一个和第二个函数,print(x);print(y);两条语句均能便于通过
//但是如果屏蔽第一个和第三个函数,print(x);编译能够通过;print(y);编译不能通过
return 0;
}
void func(int *p){}
void func(const int *s){}
int main()
{
int x=10;
const int y=20;
func(&x);//最佳匹配:func(int *p)//如果没有普通指针 func(const int *s)
func(&y);//最佳匹配:func(const int *s),如果没有常指针函数其它函数均不能与其匹配
//屏蔽第一个函数,func(&x);func(&y);两条语句均能便于通过
//但是如果屏蔽第二个函数,func(&x);编译能够通过;func(&y);编译不能通过
return 0;
}
void fun(int *p){}//1
void fun(const int *s){}//2
void fun(int * const r){}//3
//1,3是相同的函数声明,而2与其他两个不同