函数重载定义:可以有多个同名的函数。可以使用函数重载来设计一系列函数-----它们完成相同的工作,但使用不同的参数列表。
函数重载的关键是函数的参数列表----也称为函数特征标(funtion signature)
.如果两个函数的参数数目和类型相同,同时参数的排列顺序也相同,则它们的特征标相同,而变量名是无关紧要的。C++允许定义名称相同额函数,条件是它们的特征标不同。如果参数数目或者参数类型不同,则特征标也不同。
【示例】定义一组原型如下的printf()函数:
void printf(const char * str,int width); #1
void printf(double d,int width); #2
void printf(long k,int width); #3
void printf(int i,int width); #4
void printf(const char *str); #5
使用printf()函数时,编译器将根据所采取的用法使用有相应特征标的原型:
printf("hello",15); #1
printf("hello"); #5
printf(1000.0,15); #2
printf(1000,15); #4
printf(1000.L,15); #3
例如:printf(“hello”,15); 使用一个字符串和一个整数作为参数,这与#1原型匹配。
使用被重载的函数时,需要在函数调用中使用正确的参数类型。例如:
unsigned int y=100;
printf(y,6); //ambiguous call
printf()调用与哪个原型匹配呢?它不与任何原型匹配!没有匹配的原型并不会自动停止使用其中的某个函数,因为C++将尝试使用标准类型转换强制进行匹配。如果#2原型是printf()唯一的原型,则函数调用printf(y,6)将把y转换为double类型。但是在上面的代码中,有三个将数字作为第一个参数的原型,因此有三种转换y的方式。在这种情况下,C++将拒绝这种函数调用,并将其视为错误。
一些看起来彼此不同的的特征标是不能共存的。例如:
double cube(double x);
double cube(double & x);
这里不构成重载。从编译器的角度来考虑这个问题。
假如有这样的代码:
cout<<cube(x);
参数x与double x 原型和double &x原型都匹配。因此编译器无法确定究竟应使用哪个原型。为避免这种混乱,编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标
。
匹配函数时,并不区分const 和非const 变量。请看下面的原型:
void f(char *p); #1
void f(const char * p) #2
void y(char *p) #3
void k(const char * p) #4
下面列出了各种函数调用相应的原型:
cosnt char p1[20]="hell world";
char p2[20]="good ";
f(p1); #2
f(p2); #1
y(p1); #no match
y(p2); #1
k(p1); #4
k(p2); #4
f()函数原型有两个原型,一个用于const指针,另一个用于常规指针,编译器将根据实参是否为const来决定使用哪个原型。y()函数只与非const 参数的调用匹配,而k()函数可以与带const 或非const 参数的调用匹配。y()和k()之所以在行为上有这种差别,主要是由于将非const值赋给const变量是合法的,但反之是非法的。(范围可以缩小,不可以放大)。
函数的重载的规则:
- 函数名称必须相同。
- 参数列表必须不同(个数不同、类型不同、参数排列顺序不同等)。
- 函数的返回类型可以相同也可以不相同。
- 仅仅返回类型不同不足以成为函数的重载。
不可以构成重载的情况:
-
静态函数声明(static)
-
参数是引用的类型,要根据实参的情况来定
实参为常量时,可以重载 实参为变量时,不可以重载 一般而言,引用不作为重载的条件
-
参数是const的类型,也要根据具体情况分析
如果是const普通类型,则不可形成重载 如果是const指针类型,则可以形成重载
C++ 是如何做到函数重载的
C++代码在编译时会根据参数列表对函数进行重命名,例如 void Swap(int a, int b)
会被重命名为 _Swap_int_int
, void Swap(float x, float y)
会被重命名为 _Swap_float_float
。当发生函数调用时,编译器会根据传入的实参去逐个匹配,以选择对应的函数,如果匹配失败,编译器就会报错,这叫做 重载决议( Overload Resolution ) 。
不同的编译器有不同的重命名方式,这里仅仅举例说明,实际情况可能并非如此。
从这个角度讲,函数重载仅仅是语法层面的,本质上它们还是不同的函数,占用不同的内存,入口地址也不一样。