重载函数 (C++Primer-9)

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 );

与一个类型转换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---


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值