目录标题
- 为什么会有函数重载
- 函数重载的概念
- 函数重载的例子
- 第一个:参数的类型不同
- 第二个:参数的个数不同
- 第三种:类型的顺序不同
- 函数重载的奇异性
- 重载函数的底层原理
- 有关函数重载的一个问题
为什么会有函数重载
大家在学c语言的时候有没有发现一个问题就是,函数规定的是不是有点太死板了,比如说我要写一个加法函数,这个函数的作用就是实现两个整型的相加,那么我们的函数就得写成这样:
int add(int x, int y)
{
return x + y;
}
但是有时候我们不仅仅要相加整数,我们还得相加两个浮点型的小数,那么我们这里的c语言就不仅仅得改这里的形参的类型和返回的类型了,我们还得修改这里的名字,比如说我们下面的代码:
double add1(double x, double y)
{
return x + y;
}
那如果是两个字符型的相加呢?我们是不是就又得取一个新的名字了啊,那么这不就非常的麻烦嘛对吧,取名字麻烦,等我调用这些函数也非常的麻烦,所以我们就发现c语言中的一个不足就是在用函数来实现一个功能,但是这个功能在面向不同的数据类型或者个数的时候往往得创建多个函数出来,而且这些函数的函数名不能一样,这对于一些取名困难户来说可真是天塌了,那么为了解决这个问题我们的c++就提出来了函数重载这个概念。
函数重载的概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
函数重载的例子
通过上面的概念我们知道了实现函数重载的前提是同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,那么我们这里就来一一举例子:
第一个:参数的类型不同
int add(int x, int y)
{
return x + y;
}
double add1(double x, double y)
{
return x + y;
}
上面的这两个加法函数就构成了重载,他们的函数名相同,参数的个数相同,但是他们的参数类型不同,所以构成了重载。
第二个:参数的个数不同
void f()
{
cout << "f()" << endl;
}
void f(int a)
{
cout << "f(int a)" << endl;
}
那么我们这两个函数的名字相同,但是函数中的参数不同所以也可以构成重载。
第三种:类型的顺序不同
void f(int a, char b) {
cout << "f(int a,char b)" << endl; }
void f(char b, int a) {
cout << "f(char b, int a)" << endl; }
这里的函数名相同,参数的类型相同参数的数目相同,但是他的类型的顺序不同所以我们这里也可以构成重载,但是这里要注意的一点就是我们这里的顺序不同指的是类型的顺序不同,大家不要以为形参名的顺序不同也能构成重载了啊,这个是不行的。
函数重载的奇异性
大家首先来看看下面的代码:
void func()
{
cout << "func()" << endl;
}
void func(int a = 0, int b = 1)
{
cout << "func(int a, int b)" << endl;
}
我们首先来看判断一个问题就是我们这两个函数是否构成函数的重载,判断重载就得先来判断一下函数的参数是否满足条件,那我们发现这两个函数的参数个数不一样,函数名却相同所以我们这里就可以构成函数的重载,既然可以构成函数重载的话,那按道理我们这里就应该可以正常地调用这两个函数,比如说下面地代码我们就可以正常地调用第二个func函数:
#include<iostream>
using namespace std;
void func()
{
cout << "func()" << endl;
}
void func(int a = 0, int b = 1)
{
cout << "func(int a, int b)" << endl;
}
int main()
{
func(10, 20);
}
我们可以看到这里正常地执行了第二个函数:
那按照同样地道理我们这里应该也可以调用这里地第一个函数,第一个函数没有参数所以我们这里在调用他地时候就不用传参,那么我们地代码就如下:
#include<iostream>
using namespace std;
void func()
{
cout << "func()" << endl;
}
void func(int a = 0, int b = 1)
{
cout << "func(int a, int b)" << endl;
}
int main()
{
/*func(10, 20);*/
func();
}
但是我们将这个代码运行起来就会发现这里报出来错误:
我们仔细看一下这个错误就会发现它这里说地就是对重载函数地调用不明确,换一个意思说就是编译器不知道要调用哪个函数,那为什么会出现这种情况?我们这里要调用第一个func函数,这个函数在声明地时候是没有参数的,所以在调用它的时候就不对其提供实参,但是这时候来看看第二个函数它虽然是有参数的,但是他的两个参数我们都对其提供了缺省参数,所以我们在调用第二个函数的时候可以对其进行传参,也可以不对其传参,但是这样的话我们调用第一个函数的时候,也会调用第二个函数啊,所以我们这里的编译器就会报出错误说对重载函数的调用不明确,所以我们这里就称为重载函数的奇异性,这里确实构成了重载但是我们在调用函数的时候依然会出现问题,所以大家在写重载函数的时候得注意一下这个问题。
重载函数的底层原理
通过之前的学习我们知道函数在调用的时候都会通过call这个指令来调用一些函数,比如说我们下面的代码:
#include<stdio.h>
int add(int x, int y)
{
return x + y;
}
int main()
{
add(2, 3);
return 0;
}
我们对其转到反汇编就可以看到这里有个call指令,该指令就是用来调用add这个函数后面的一连串字母加数字就是这个函数的地址
通过之前的学习我们还知道程序在编译的过程中会生成一个东西叫符号表,这个符号表里面就记录着各种函数和全局变量的名字和地址,而我们c语言在记录这些地址的时候就非常的简单对函数名做出来的修改很少,所以当我们用c语言写两个函数名一样但是参数不同的函数的时候我们的符号表上对应的函数是一样的,但是我们的编译器他是不允许符号表上有两个同样的名字,所以我们的c语言是不支持函数的重载的,所以c++就对其进行了升级,他对这个符号表上的名字进行了一些修改,之前c语言符号表上的名字可能就是简简单单的一个名字本身,但是c++在函数名的基础上还将其参数的类型个数也加了上去比如说我们下面的图片:
c++编译的结果就如下:
我们发现函数的名字后面有多了几个字符,这个字符就是我们函数参数的缩写,第一个函数的参数是两个int所以就在后面加了两个i,因为add函数名是三个字母所以这里还在函数名前面加了一个3,那我知道的改变就是这些,至于前面的z是啥我也不知道,那通过这个改变大家应该能够明白为什么我们函数重载的条件得是参数个数 或 类型 或 类型顺序不同了,因为这些不同带来的结果就是在符号表中对应的名字不同,这样我们调用的时候就可以根据参数的类型来找这些我们想调用的同名的重载函数了。那这就是函数重载的底层原理。
有关函数重载的一个问题
大家有没有想过一个问题就是:为什么函数名相同 参数相同 但是返回值不同的函数却不能构成重载呢?有些小伙伴说啊,因为我们这里的底层逻辑没有对返回值添加相应的修改,所以无法构成重载,但是如果我们这里对其添加相应的修改呢?我们不同的返回值就在符号表的名字上加上不同的标识,这样不也可以吗?但是为什么我们的编译器没有这么做呢?那么大家这样想,如果我们这里有两个函数他们的参数个数一样类型一样顺序也一样的话,就返回值的类型不同,那我们在对其进行调用的时候是不是参数就一样了啊,那我们在调用函数的时候编译器怎么知道我想调用的是返回值为int类型的函数还是返回值为double的函数呢?对吧所以这就是不能构成重载的原因,希望大家能够理解。