9 重载函数
函数名字相同, 并且在相同的域中被声明, 但是参数表不同, 称为重载函数overloaded function;
9.1 重载函数声明
9.1.1 为什么要重载函数名
作用在不同类型的参数上的一般性的动作;
9.1.2 怎样重载函数名
重载: 函数的参数表中参数的个数或类型不同;
如果同名函数的参数表精确匹配, 会被视为重复声明(error);
Note 参数表相同, 返回值类型不同属于重复声明;
只有缺省实参不同, 属于重复声明;
Note 按值传递时, 参数类型有const和volatile修饰符, 不被考虑成重载;
按指针或按引用传递时, const或volatile修饰符会影响重载;
9.1.3 何时不重载
提供不同函数名使得程序便于理解;
9.1.4 重载与域
重载函数集合中的全部函数应该在同一个域中声明;
Note 声明为局部的函数将隐藏全局域中声明的同名函数;
每个类都维持着自己的一个域, 所以两个不同类的成员函数不能重载;
不同名字空间的成员函数不能重载;
using声明和using指示符对重载函数的声明有影响;
using声明为名字空间的成员在其他域中提供了一个别名, 不能在using声明中为函数指定参数表;
这样是为了保护名字空间的接口, 保证所有的重载函数被一起声明;
using mySpace::Fuc; //不能加参数表(int&)
using声明引入的函数被视作在该声明出现的地方被声明;
Note 如果using声明向域中引入一个函数, 该域中已经有同名函数并具有相同参数表, 则using声明会产生错误;
using指示符把所有声明加入到当前域中, 如果当前域有相同函数名, 则把名字空间的成员函数加入到重载函数集;
using namespace libs_R_us;
9.1.5 extern "C"和重载函数
链接指示符extern "C"表示C++程序中的某一函数是用程序设计语言C编写的;
Note 链接指示符只能指定重载函数集中的一个函数;
9.1.6 指向重载函数的指针
extern void ff( vector<double> );//1)
extern void ff( unsigned int );//2)
void ( *pf1 )( unsigned int ) = &ff;//指向2)
void ( *pf2 )( int ) = &ff;// 错误: 无匹配: 无效参数表
double ( *pf3 )( vector<double> ) = &ff;// 错误: 无匹配: 无效返回类型
对于重载函数的地址赋值给函数指针的情况:
函数指针的类型必须与重载函数类型匹配, 函数指针类型不能转换;
9.1.7 类型安全链接
编译器会将函数名及其相关参数表作为一个唯一的内部编码encoded;
帮助编译器链接阶段区分重载函数, 称为类型安全链接type-safe linkage;
extern "C"声明的函数没有特殊编码;
9.2 重载解析的三个步骤
函数重载解析function overload resolution是把函数调用与重载函数集合中的一个函数相关联的过程;
根据实参选择一个函数; 检查二义性ambiguous;
函数重载解析步骤:
1)确定函数调用的重载函数的集合, 确定函数调用中实参表的属性;
2)从重载函数集合中选择函数(根据实参);
3)选择与调用最匹配的函数;
-1)集合中的函数称为候选函数candidate function(与被调函数同名, 声明可见);
-2)选出的函数称为可行函数viable function(参数个数与实参表相同或者更多, 多出的参数有缺省实参); 对于可行参数, 调用的实参与该函数的参数类型之间必须可以转换conversion;如果没有找到可用的可行函数, 则调用产生错误, 无匹配情况no match situation;
-3)选择最匹配的函数, 最佳可行函数best viable function/最佳匹配函数best match function;
实参类型到可行函数参数类型的转换被划分等级ranked:
1)应用在实参上的转换不比其他可行函数所需的转换差;
2)在某些实参上的转换比其他可行函数对该参数的转换好;
9.3 参数类型转换
1 精确匹配exact match
实参与韩式参数的类型精确匹配;
void print( unsigned int );
void print( const char* );
void print( char );
unsigned int a;
print( 'a' ); // 匹配 print( char );
print( "a" ); // 匹配 print( const char* );
print( a ); // 匹配 print( unsigned int );
2 与一个类型转换type conversion匹配;
实参不直接与参数类型匹配, 但是可以通过类型转换匹配; e.g. void fuc(char); fuc(65); //int-->char;
3 无匹配no match
实参不能与声明的函数的参数匹配, 无法进行类型转换; e.g. int-->int*;
精准匹配的等级类别可能存在的转换:
-左值到右值的转换;-数组到指针的转换;-函数到指针的转换;-限定修饰转换;
与类型转换匹配的等级类别中:
提升promotion; 标准转换standard conversion; 用户定义的转换user definded conversion;
9.3.1 精准匹配的细节
实参与函数参数类型精准匹配;
枚举类型定义了一个唯一的类型, 只与枚举类型中的枚举值已经被声明为该枚举类型的对象精准匹配;
左值代表一个可被程序寻址的对象, 可从该对象读取一个值; 右值是一个表达式, 表示一个值, 或一个引用了临时对象的表达式, 不能寻址, 不能改变它的值;
1)左值到右值的转换
int sum = obj1+obj2; //左值表达式;
一个引用表示一个左值, 当函数有引用参数时, 被调用的函数接受左值, 没有转换;
2)数组到指针的转换
函数参数没有数组类型, 参数会被转换成指向数组首元素的指针; 数组到指针的转换被看作精准匹配;
3)函数到指针的转换
函数类型的参数和会自动转换成函数指针;
4)限定修饰符转换
这种转换只影响指针; 将限定修饰符const或volatile(或两者)加到指针指向的类型上;
int a[5] = { 4454, 7864, 92, 421, 938 };
int *pi = a;
bool is_equal( const int * , const int * );
//---
void func( int *parm ) {
is_equal( pi, parm );
}
>实参pi和parm从(int*)转换成(const int*), 属于限定符转换的精准匹配;
对于const对象, 指针常量(int* const)参数, 不会出现转换, 属于精准匹配;
1)-3)被称为左值转换 lvalue transformation; 左值转换精确匹配>限定修饰符转换;
精确匹配可以用显式强制转换强行执行;
extern void ff(int);
extern void ff(void *);
//---
ff( reinterpret_cast<void *>(0xffbc) ); // 调用 ff(void*)
9.3.2 提升的细节
char, unsigned char, short --> int; 如果机器上int字长比short长, unsigned short-->int; 否则 unsigned short --> unsigned int;
float-->double;
enum-->提升到能够表示其所有枚举常量的类型 int, unsigned int, long, unsigned long;
bool-->int;
当实参是上面的源类型之一, 函数参数的类型是被提升的类型时, 应用该提升;
extern void manip( int );
manip( 'a' ); //char-->int
Note 枚举类型的提升需要根据枚举常量的值的类型来决定;
e.g. 1-->int, 0x800000000-->unsigned int
9.3.3 标准转换的细节
标准转换:
1)整值类型; 2)浮点转换; 3)浮点-整值;
4)指针转换: 0到指针类型, 指针类型到void*;
5)bool转换: 整值, 浮点, 枚举到bool型;
1)2)3)转换的目标类型不能表示源类型全部值的情况下是有潜在危险的;
>所有的标准转换都是相同优先级;
extern void manip( long );
extern void manip( float );
manip( 3.14 ); // 错误: 二义性
>使用显示强转解决二义性;
manip( static_cast<long>( 3.14 ) ); // manip( long )
manip( 3.14F ); // manip( float )
空指针值null pointer value 0可以被转换成任何指针类型;
void set(int*);
// 从0 到int* 的指针转换应用到两个实参上
set( 0L );
set( 0x00 );
>常量表达式0L(long int的0)和0x00(十六进制的0)都属于整型类型, 可以转换成int*;
enum EN { zr = 0 };
set( zr ); // 错误: zr 不能被转换到 int*
Note 枚举类型不是整型;
void*是通用的数据类型指针, 可以存放任何数据类型的指针值;
Note 函数指针不能标准转换成void*, 只有指向数据类型的指针才可以转换;
9.3.4 引用
Note 实参是引用时, 是一个左值, 类型是引用所指的对象的类型, 类型不会是引用类型;
实参与引用参数的匹配结果:
1)实参是引用参数合适的初始值--精准匹配;
2实参不能初始化引用参数--不匹配, 无法调用;
int obj;
void frd( double & );
frd( obj ); // 错误: 参数必须是 const double &
>实参是int, 到double转换的结果是个临时值, 无法初始化非const引用;
class B;
void takeB( B& );
B giveB();
takeB( give() ); // 错误: 参数必须是 const B&
>实参是函数调用的返回值, 是一个临时值, 无法初始化非cosnt引用;
9.4 函数重载解析细节
三个步骤
1)确定为该调用而考虑的候选函数, 函数调用中的实参表属性;
2)候选函数中选出可行函数, 根据实参, 实参数目和类型选择;
3)对实参到函数参数类型的转换划分等级, 选出最匹配;
9.4.1 候选函数
1)在调用点可见(范围)的同名函数;
2)在实参类型所在的名字空间中声明的同名函数;
Note 在嵌套的域中被声明的函数隐藏外围域中的同名函数;
char* format( int );
void g() {
char* format( double );
char* format( char* );
format(3); // 调用 format( double )
}
namespace
namespace libs_R_us {
int max( int, int );
double max( double, double );
}
char max(char, char);
void func()
{
using libs_R_us::max; //using声明, 表示函数声明; 会隐藏外部函数;
using namespace libs_R_us; //using指示符解除名字空间; 外部函数和空间内函数都可见;
}
9.4.2 可行函数
可行函数在候选函数集合中, 参数表与调用的实参数目相同或者有更多参数; 额外的参数要有缺省实参;
如果没有可行参数, 会导致编译时错误;
9.4.3 最佳可行函数
最佳可行参数具有与实参类型匹配最好的参数;
转换序列conversion sequence
类型转换等级: 精确匹配 > 提升 > 标准转换;
转换序列组成: 左值转换, 提升或标准转换, 限定修饰转换;
左值转换 lvalue transformation: 左值到右值, 数组到指针, 函数到指针;
标准转换序列和用户定义转换序列;
9.4.4 缺省实参
可以使多个函数进入到可行函数的集合中;
---End---