[c++]——函数重载

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机制引入的命名空间中的候选函数,否则结束

  • 5
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值