函数探幽——函数重载(含重载引用参数)

8.4函数重载

函数重载让您能够使用多个同名的函数。
我们可以通过函数重载来设计一系列函数——它们完成相同的工作,但使用不同的参数列表。
函数重载的关键是函数的参数列表——也称为函数特征标(function signature)。
如果两个函数的参数数目和类型相同,同时参数的排序顺序也相同,则它们的特征标相同,
而变量名时无关紧要的。C++允许定义名称相同的函数,条件是它们的特征标不同。
如果参数数目和/或参数类型不同,则特征标也不同。例如,可以定义一组原型如下的print( )函数:

void print (const char * str, int width); //#1
void print (double d, int width); //#2
void print (long l, int width); //#3
void print (int i, int width); //#4
void print(const char str); //#5

使用print()函数时,编译器将根据采取的用法使用有相应特征标的原型:
print(“Pancakes”, 15); //use #1
print(“Syrup”); //use #5
print(1999.0, 10); //use #2
print(1999, 12); //use #4
print(1999L, 15); //use #3

例如,print(“Pancakes”, 15)使用一个字符串和一个整数作为参数,这与#1原型匹配。
使用被重载的函数时,需要在函数调用中使用正确的参数类型。例如,对于下面的语句:
unsigned int year = 3210;
print(year, 6); //ambiguous call

print()不与任何原型匹配。没有匹配的原型并不会自动停止使用其中的某个函数,因为C++尝试使用标准类型转换强制进行匹配。如果#2原型是print( )唯一的原型,则函数调用print(year,6)将把year转换为double类型。但在上面的代码中,有3个将数字作为第一个参数的原型,因此有3中转换year的方式。在这种情况下,C++将拒绝这种函数调用,并将其视为错误。

一些看起来彼此不同的特征标是不能共存的。
例如,请看下面的两个原型:
double cube (double x);
double cube (double & x);
假如有下面的代码:
cout << cube (x);
参数x与double x原型和double &x原型都匹配,因此编译器无法确定究竟应使用哪个原型。为避免这种混乱,编译器在检查函数特征标时,将把类型引用和类型本身视为同一个特征标。

匹配函数时,并不区分const和非const 变量。
请看下面的原型:
void dribble (char * bits); //overloaded
void dribble (const char * cbits); //overloaded
void dabble (char * bits); //not overloaded
void drivel (const char * bits); //not overloaded

下面列出了各种函数调用对应的原型:
const char p1 [20] = “How’s the weather?”;
char p2[20] = “How’s business?”;
dribble(p1); //dribble(const char );
dribble(p2); //dribble(char );
dabble(p1) //not match
dabble (p2); //dabble(char )
drivel(p1); //drivel(const char );
drivel(p2); //drivel(const char );

dribble( )函数有两个原型,一个用于const指针,另一个用于常规指针,编译器根据实参是否为const来决定使用哪个原型。dribble()函数只与带非const参数的调用匹配,而drivel()函数可以与带const或非const参数的调用匹配。drivel()和dabble()之所以在行为上有这种差别,主要是由于将非const值赋给const变量是合法的,但反之则是非法的。
请记住,是特征标,而不是函数类型使得可以对函数进行重载。例如,下面的两个声明是互斥的:
long gronk (int n, float m); //same signatures
double gronk(int n, float m); //hence not allowed
因此,C++不允许以这种方式重载gronk()。返回类型可以不同,但特征标也必须不同:
long gronk (int n, float m); //different signatures,
double gronk(float n, float m ); //hence allowed

重载引用参数

类设计和STL经常使用引用参数,因此知道不同引用类型的重载很有用。请看下面的三个原型:
void sink (double & r1);		//matches modifiable lvalue
void sank (const double & r2);	//matches modifiable or const lvalue, rvalue
void sunk (double && r3);		//matches rvalue
左值引用参数r1与可修改的左值参数(如double变量)匹配;
const左值引用参数r2与可修改的左值参数、const左值参数和右值参数(如两个double值的和)匹配;
最后,左值引用参数r3与左值匹配。注意到与r1和r3匹配的参数都与r2匹配。
这就带来了一个问题:如果重载使用这三种参数的函数,结果将如何?答案是将调用最匹配的版本:
void staff (double & rs);			//matches modifiable lvalue
void staff (const double & rcs)		//matches rvalue, const lvalue
void stove(double & r1);			//matches modifiable lvalue
void stove(const double & r2);		//matches const lvalue
void stove(double && r3);			//matches rvalue
这让您能够根据参数是左值,const还是右值来定制函数的行为:
double x = 55.5;
const double y = 32.0;
stove(x);			//calls stove(double &)
stove(y);			//calls stove(const double &)
stove(x+y);		//calls stove (double &&)
如果没有定义函数stove(double &&), stove(x+y)将调用函数stove(const double &).
何时使用函数重载
仅当函数基本上执行相同的任务,但是用不同形式的数据时,才应采用函数重载。

什么是名称修饰

C++如何跟踪每一个重载函数呢?它给这些函数指定了秘密身份。
使用C++开发工具中的编辑器编写和编译程序时,C++编译器将执行一些神奇的操作——名称修饰
(name decoration)或名称矫正(name mangling),它根据函数原型中指定的形参类型对每个函数名进行加密。
请看下述未经修饰的函数原型:
long MyFunctionFoo(int, float);
这种格式对于人类来说很适合;我们知道函数接受两个参数(一个为int类型,另一个为float类型),
并返回一个long值。而编译器将名称转换为不太好看的内部表示,来描述该接口,如下所示:
?MyFunctionFoo@@YAXH
对原始名称的表面看来无意义的修饰(或矫正)将对参数数目和类型进行编码。
添加的一组符号随函数特征标而异,而修饰时使用的约定随编译器而异。

By——Suki
From《C++ Primer Plus》

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值