####################
1. 问题描述
函数重载是指在同一作用域内,可以有一组具有相同函数名,不同参数列表的函数,这组函数被称为重载函数。那为什么不可以是函数名相同,参数列表相同,函数的返回值不同呢?
2. 从一个函数重载实例说起
看下面的一个例子,来体会一下:实现一个打印函数,既可以打印int型、也可以打印字符串型。在C++中,我们可以这样做:
-
-
using namespace std;
-
-
void print(int i)
-
{
-
cout<< "print a integer :"<<i<< endl;
-
}
-
-
void print(string str)
-
{
-
cout<< "print a string :"<<str<< endl;
-
}
-
-
int main()
-
{
-
print( 12);
-
print( "hello world!");
-
return 0;
-
}
3. 为什么需要函数重载(why)?
- 试想如果没有函数重载机制,如在C中,你必须要这样去做:为这个print函数取不同的名字,如print_int、print_string。这里还只是两个的情况,如果是很多个的话,就需要为实现同一个功能的函数取很多个名字,如加入打印long型、char*、各种类型的数组等等。这样做很不友好!
- 类的构造函数跟类名相同,也就是说:构造函数都同名。如果没有函数重载机制,要想实例化不同的对象,那是相当的麻烦!
- 操作符重载,本质上就是函数重载,它大大丰富了已有操作符的含义,方便使用,如+可用于连接字符串等!
通过上面的介绍我们对函数重载,应该唤醒了我们对函数重载的大概记忆。下面我们就来分析,C++是如何实现函数重载机制的
4. 函数重载为什么不考虑返回值类型
为了弄清楚这个问题,我们先看看声明/定义重载函数时,是如何解决命名冲突的?
为了了解编译器是如何处理这些重载函数的,我们反编译下上面我们生成的执行文件,看下汇编代码(全文都是在Linux下面做的实验,Windows类似,你也可以参考《一道简单的题目引发的思考》一文,那里既用到Linux下面的反汇编和Windows下面的反汇编,并注明了Linux和Windows汇编语言的区别)。我们执行命令objdump -d a.out >log.txt反汇编并将结果重定向到log.txt文件中,然后分析log.txt文件。
发现函数void print(int i) 编译之后为:(注意它的函数签名变为——_Z5printi)

发现函数void print(string str) 编译之后为:(注意它的函数签名变为——_Z5printSs)
我们可以发现编译之后,重载函数的名字变了不再都是print!这样不存在命名冲突的问题了,但又有新的问题了——变名机制是怎样的,即如何将一个重载函数的签名映射到一个新的标识?我的第一反应是:函数名+参数列表,因为函数重载取决于参数的类型、个数,而跟返回类型无关。但看下面的映射关系:
void print(int i) --> _Z5printi
void print(string str) --> _Z5printSs
进一步猜想,前面的Z5表示返回值类型,print函数名,i表示整型int,Ss表示字符串string,即映射为返回类型+函数名+参数列表。最后在main函数中就是通过_Z5printi、_Z5printSs来调用对应的函数的:
80489bc: e8 73 ff ff ff call 8048934 <_Z5printi>
……………
80489f0: e8 7a ff ff ff call 804896f <_Z5printSs>
我们再写几个重载函数来验证一下猜想,如:
void print(long l) --> _Z5printl
void print(char str) --> _Z5printc
可以发现大概是int->i,long->l,char->c,string->Ss….基本上都是用首字母代表,现在我们来现在一个函数的返回值类型是否真的对函数变名有影响,如:
-
-
using namespace std;
-
-
int max(int a,int b)
-
{
-
return a>=b?a:b;
-
}
-
-
double max(double a,double b)
-
{
-
return a>=b?a:b;
-
}
-
int main()
-
{
-
cout<< "max int is: "<<max( 1, 3)<< endl;
-
cout<< "max double is: "<<max( 1.2, 1.3)<< endl;
-
return 0;
-
}
-
float sqrt( float);
-
double sqrt( double);
-
-
void f( double da, float fla)
-
{
-
float fl=sqrt(da); //调用sqrt(double)
-
double d=sqrt(da); //调用sqrt(double)
-
-
fl=sqrt(fla); //调用sqrt(float)
-
d=sqrt(fla); //调用sqrt(float)
-
}
5. 总结
从重载的机制来看,并不是没法实现返回值类型不同情况下的函数重载,只是从独立于上下文的出发点考虑,所以才没有支持。
关于c++函数重载的更多介绍可以参考C++的函数重载。