2019.80.30第一次修改:更改修饰名称错误问题,简化匹配重载函数集描述
c++的函数重载
先来看几个问题:
1.什么是函数重载?
2.为什么需要函数重载?
3.函数重载为什么不考虑返回类型?
4.编译器如何解决命名冲突?
5.编译器如何解析函数重载调用?
1.什么是函数重载?
解释:函数重载是指在相同的作用域内,函数名相同,参数列表不同的一组函数,这组函数被称为重载函数。
ps:重载函数通常用来命名一组功能相似的函数,这样减少了函数名的数量,避免了名字空间的污染,对程序的可读性有很大的好处
#include<iostream>
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;
}
2.为什么需要函数重载?
2.1命名的便利性
假设我们没有函数重载,那么我们如果打印int和char类型时我们就需要写俩个函数名不一样的函数(printf_char printf_int),如果我们需要打印更多的类型,那么我们就要起更多不同的函数名,这是非常不友好的。
2.2类的构造函数
在c++中实例化一个对象时,他们的构造函数都是重名的,如果没有函数重载我们在实例化不同的对象时是非常麻烦的
2.3操作符重载
事实上操作符的重载就是函数的重载,有了函数重载我们可以让操作符的含义更丰富
3.函数重载为什么不考虑返回类型
解释:为了函数调用时独立于上下文(不依赖于上下文)
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)
}
4.编译器怎么解决命名冲突
我们编译以下代码后
void print(int i)
查看编译后的函数名
_Z可以理解为Linux下的一种特殊命名生成标记,5为函数名的长度,printf是函数名,i则是参数列表的类型缩写
所以此时我们可以认为编译器对函数名的命名规则是:_Z+函数名长度和函数名+参数列表
但是我们将重载函数放入一个类中:
#include<iostream>
using namespace std;
namespace N
{
class test{
public:
void print(int i)
{
cout<<"int"<<endl;
}
void print(char c)
{
cout<<"char"<<endl;
}
};
}
int main()
{
N::test t;
N::t.print(1);
N::t.print('a');
return 0;
}
再次编译后我们发现多了一个N4test
从void print(int i) 转化为 _ZN4test5printEi 我们进一步分析出:N表示函数处于哪个命名空间,4是类名的长度,所以我们最后分析出,Linux下对与函数名的重载修饰规则为
_Z + 作用域 + 类名长度和类名 + 函数名长度和函数名 + 参数列表
有兴趣的同学下去可以看看windows下vs的修饰规则:
windows:?函数名 + 类名@ + 作用域名字@ + @ + 参数类型 + @Z
5.编译器如何解析函数重载调用函数
5.1重载函数的匹配规则
1.精确匹配:参数匹配而不做转换,或者只做微小的转换,如:数组名到指针,函数名到指针
2.提升匹配:从bool类型到int,char到int,short到int
3.标准转换匹配:int 到double、double到int、double到long double、Derived到Base、T到void、int到unsigned int
4.用户自定义匹配
5.使用省略号匹配
当我们在调用匹配时如果有多个函数被找到,调用将被拒绝(含有歧义),定义太少或太多的重载函数,都有可能导致模凌两可
此时调将被拒绝!
int pow(int ,int);
double pow(double,double);
void g()
{
double d=pow(2.0,2)//调用pow(int(2.0),2)? pow(2.0,double(2))?
}
编译器如何解析函数重载调用函数?
分为4步:
- 首先我们要匹配函数调用,获取函数名
- 获得各函数参数的表达式类型
- 经过 重载解析 返回最佳的参数
- 将符号表中存储的最佳函数绑定到抽象语法树上
什么是重载解析?
根据函数名确定候选函数集,从候选函数集中选择可用函数集合从可用函数集中确定最佳函数,或由于模凌两可返回错误
根据函数名确定候选函数集
根据函数在同一作用域内所有同名的函数,并且要求是可见的(像private、protected、public、friend之类)。“同一作用域”也是在函数重载的定义中的一个限定,如果不在一个作用域,不能算是函数重载,如下面的代码:
void f(int);
void g()
{
void f(double);
f(1); //这里调用的是f(double),而不是f(int)
}
为了查找候选函数集,一般采用深度优选搜索算法
深度优先搜索算法:
1.从函数的调用点开始,逐层作用域向外查找可见的候选函数
2.如果上一步收集的不在用户自定义命名空间中,则用到了using机制引入的命名空间中的候选函数,否则结束