C++重载函数的定义:同一作用域的多个函数,如果具有相同函数名而同时具有不同的形参列表,它们可以称为重载函数。
1 重载函数黑体关键词解析
1.1 相同函数名
1)重载函数也是函数,所以需要函数名;函数名只是为了帮助编译器判断调用的是哪个函数而已。
2)相同函数名正是重载函数引入的目的,对于一系列类似的操作省去为函数起名和记住函数名的麻烦,简化了程序实现,且使程序更容易理解。
1.2 同一作用域
重载函数必须在同一作用域的原因是:C++有局部声明屏蔽全局声明的特性。这样如果在局部作用域有一个与外层作用域的重载函数同名的函数,该函数将屏蔽那些外层的重载函数。
例子如下:
void func(const string &);
void func(const double); //重载func
void f(int ival){
void func(int); //局部声明
func(”hello“);//error:因为func(const string &)被屏蔽了
func(ival); //ok:局部的func(int)可见
func(1.5);//ok:尽管全局重载函数func(const double)被屏蔽,但是可调用局部func(int),调用时实参1.5隐式转化为int型
}
但是如果将局部声明拿到函数外,位于同一作用域后,重载函数ok。
void func(const string &);
void func(const double); //重载func
void func(int); //重载func
void f(int ival)
{
func(”hello“);//ok:调用func(const string &)
func(ival); //ok:调用func(int)
func(1.5);//ok:调用func(const double)
}
1.3 不同形参列表
代码经过编译后会生成符号(symbol)表,函数调用是通过符号表中函数符号名来实现的(注意:函数名不同于函数符号名)。 C++中的函数符号名由函数名和形参列表共同决定(函数符号名与函数返回值没有任何关系),形参列表包括形参个数和形参类型。
重载函数正是利用C++函数符号名这种特性来实现的,这样相同函数名如果使用不同形参列表,会生成不同函数符号名(不会出现重复定义的问题)。
判断不同形参列表时注意事项(注意下面people是类):
(1)形参名仅是帮助文档,不会影响形参列表
void func(const people &a); //形参名a
void func(people &);
(2)typedef仅提供别名,不会创建新的类型
typedef people p //p是people的别名
void func(const people &);
void func(const p &);
(3)调用带默认实参函数时,编译器可以忽略默认实参(这意味着用户可以给定实参也可以不给定),默认实参不影响参数个数
void func(const people1 &, const people2 &);
void func(const people1 &, const people2 & = ”“); //没有改变参数个数
(4)const形参
a)对于函数值传递形参场合,const与非const是等价的形参;
void func(people1,people2);
void func(const people1, const people2);
b)对于函数引用传递形参场合,const与非const是不同的形参;
void func(people &);
void func(const people &); //新函数,是func的重载函数
const people a(1,2);
people b;
func(a);//调用func(const people &)
func(b);//调用func(people &)
c)对于函数指针传递形参场合,指向const对象与指向非const对象是不同的形参;
注意:基于指针本身是否为const不能实现函数重载
void func(int *);
void func(int * const); //属于重复声明
2 重载函数的函数匹配与实参类型转换
匹配的结果有3种可能
1)编译器找到最佳匹配函数,生成调用函数代码;
2)编译器找不到匹配函数,编译出错;
3)找到多个匹配函数,但是没有一个是最佳匹配函数,调用具有二义性;
2.1 重载函数匹配3个步骤
void func();
void func(int);
void func(int, int);
void func(double, double = 1.2);
func(1.35); //调用func(double, double = 1.2)
1)查找候选函数
与被调用函数同名的函数都可以称为候选函数。上面例子中候选函数有4个。
2)确定可行函数
可行函数需要满足2个条件:
a)被调函数实参与函数形参个数相同;
b)被调函数实参与函数参数类型匹配,或者可以通过隐式转化为类型匹配 ;
3)决定最佳匹配
最佳匹配原则:
a)实参与形参类型精确匹配优于需要类型转化后的匹配
b)多参数最佳匹配特点
i)每个实参匹配不劣于其它可行函数;
ii)至少有一个实参优于其它可行函数;
func(1,1.5)调用分析:
第一步候选函数:4个
第二步:可行函数func(int,int)和func(double,double)
第三步:最佳匹配,只看实参数1精确匹配func(int,int),再看实参2精确匹配的是func(double,double),2个函数形参优劣相当这样找不出最佳匹配,出现了”二义性“。
解决二义性办法是显式强制类型转换,但是强制类型转换不是最好的,要避免;出现这样情况表明形参设计不合理。
func(static_cast<double>(1), 1.5)//调用func(double,double)
func(1, static_cast<int>(1.5)) //调用func(int,int)
2.2 实参类型转换
实参类型转换分为4级:
1)精确匹配,实参与形参类型一致
2)通过类型提升实现匹配;
3)通过标准转换实现匹配;
4)通过类类型转换实现匹配;
注意:
a)类型提升实现匹配优于标准转换实现的匹配
extern void func(long);
extern void func(float);
func(3.14); //造成二义性,因为都是使用标准转换,没有说哪个更优
b)较小整型提升为int型
extern void func(int);
extern void func(short);
func('c'); //char提升为int,匹配到函数func(int)
c)枚举对象只能用同类型枚举对象或者枚举成员初始化
enum Test {T1 = 1, T2 = 130};
extern void func(Test);
extern void func(int);
void main()
{
Test test = T1;
func(10);//调用func(int)
func(T1); //调用func(Test)
func(test);//调用func(Test)
return 0;
}
d)枚举值可以传递给整型形参,整型值不能传递给枚举形参
enum Test {T1 = 1, T2 = 130};
extern void func(int);
<pre name="code" class="cpp">extern void func(unsigned char);
unsigned char uchar = 130;
func(T2); //调用
func(uchar);//调用
3 参考资料
C++ primer