目录
1.1 可以进行无参调用,就跟含缺省参数的函数的无参调用一样
2.4.1预备知识:同名函数与一函多义
在了解函数重载之前,我们要知道函数重载是弥补C语言不兼容同名函数的情况,那么为啥会要求同名同名函数存在呢?且听我慢慢道来。
我们在为函数命名时常用“功能冠以名”,不然不好凭名字知道其功能,这就可能会出现功能相近似的两个函数出现名字冲突这种情况,所以常常还会对起名字加上前缀后缀等,也就是改名字(详情参见:命名空间),当然我们之前学了命名空间,那能否解决这个问题呢?答案是可以的,但有使用条件,无论是使命名空间域降级的域展开,还是替换到全局域的指定展开,亦或是一次一个的指定访问,都是使的同名的多个函数每次只能使用固定的一个,即一个函数只能有一个作用,而调用重载的函数时是通过函数名符号表+形参类型+形参个数来寻址的,这就使得在同一个域中真正地存在同名函数,但要知道的是他们虽然同名但本质上是不同两个函数的,只是因为函数寻址方式的不同而存在,函数调用时先是按函数名找,后按参数类型找,只要调用时传不同的参数,就能实现——一函多义,多函合并。
为什么要实现一函多义,多函合并呢?
1.实现功能相近而参数不同的函数合并,能加强代码的可读性,降低函数使用成本,毕竟需要记得函数种类少了,用也少了吗。
2.
2.4.2,函数重载的概念
几个同名不同形参参数顺序的函数相互构成重载,彼此互称重载函数。可见这是一种关系。
N×同名不同参数顺序的函数=>函数重载。
注意:形参顺序是指形参类型+个数组成一个有序的以类型为成员的数组。
由于函数的寻址规则在C++中有所改变,当函数名和形参顺序同时向同是才发生命名冲突。
为什么不能加上返回值类型作为区别依据呢?时间上在函数寻址就没有考虑返回值,当然也无法找到,这是因为函数调用时就没有返回值的要求,编译器通过函数调用所给的信息来寻址。
2.4.3,函数重载的格式
2.4.4,重载函数的调用端
1一函多义
重载函数在调用时在形式上看似是调用一个函数的,实质是调用多个,这就在函数的调用端,或者说应用端实现了一函多义。
func函数看是能实现打印两个浮点型或整型,但那是只是两个函数调用的结果,这也说明了C++的实现层与应用层分离的更彻底。
1.1 可以进行无参调用,就跟含缺省参数的函数的无参调用一样
引入无形参的通同名函数实现重载。但不要这么做,详情见下文2.4.5。
2 隐式类型转换的冲突问题
当调用函数传参数时,字符型与整型可以进行类型的相互转化,但前提是转化的方向是确定的,不然会因为转换方向的二义性产生冲突。
隐式转换就只用一个方向
#include"nest.h"
//以下的函数使用一个就行,方向就在会只有一个
//void func(int a, int b )//半缺省时,缺省参数只能由右到左,否则不能部分传参调用
//{
// cout << a << ',' << b << endl;
// printf("\n");
//}
void func(double a,double b )
{
cout << a << ',' << b << endl;
printf("\n");
}
转换方向有两个
#include"nest.h"
//两个函数构成重载,方向有两个
void func(int a , int b , int c )
{
cout << a << endl << b << endl << c << endl << a + b + c << endl;
printf("\n");
}
void func(int a, int b )//半缺省时,缺省参数只能由右到左,否则不能部分传参调用
{
cout << a << ',' << b << endl;
printf("\n");
}
void func(double a,double b )
{
cout << a << ',' << b << endl;
printf("\n");
}
2.4.5,函数重载与缺省参数
已经构成重载关系的函数,在其中多个函数增加缺省参数后仍构成重载关系,但是没有实用价值,不要这么做,为什么呢?重载函数可以进行无参调用的(见上文,2.4.4),全缺省函数也可以,这就使得两者在功能,用法上产生重叠,进而使得不传参调用时产生二义性,调那个函数谁也不知道。
2.4.6 函数重载的意义
合并成类的思想:功能相近的函数合并成一类函数。
2.4.7函数重载实现的原因——函数名修饰规则的不同
2.4.7.1函数名修饰规则的使用场景
C++能实现重载而C语言不行就是因为两者的函数寻址规则不同,是按照修是好的函数名作为地址的代名词寻找函数以访问的 。
这就涉及了编译连接过程:
1,预处理:拷贝+各.c文件拷贝份头文件展开,宏替换,条件编译,去注释生成 多个.i文件(C++代码)
//
2. 编译:各.i文件检查语法,生成汇编代码生成各.s文件(汇编指令代码)。报错就在这时候,只有执行到这时候才能反汇编,编译也是最复杂的,详情参见编译原理。
当要执行调函数的汇编指令时,需要call 函数的地址,但是函数会定义在(声明与定义分离实现应用层与实现层分离,便于分层管理)(也就是多文件才是)另一个.s文件所对应的内存上,这就使得当前阶段无法获取函数地址,当前程序处于阻塞状态,若有该函数的声明(担保人,CPU你执行吧,没事的(当然有无定义得看另一个.s文件,有没有我不管)),使用声明产生的函数名修饰规则而的字符串占据要接受地址的地方,就继续执行其他汇编指令。
3. 汇编:
各.s文件转换成二进制指令码的.o文件。(编译器在此时产生符号表 )
4,链接:
各个.o文件合并+内部再未链接的链接(eg.函数按照 使用函数名修饰规则而的字符串再找地址,是时候兑现承诺了) =》生成一个.exe可执行文件(Linux下为.out文件)
当链接是未找到报链接错误(只用声明无定义),非编译错误(语法错误)。
?fub@@YAXXZ
这就是VS下fub函数由函数名修饰规则的产物。objdump -S .aut文件
2.4.7.2 函数名修饰的规则和寻址的使用
2.4.7.2.1 函数名修饰的规则
C++ 看函数名+参数列表 (名参不同,修饰的名不同,地址不同)
Linux _Z+函数名字符个数+函数名+参数列表中各参数首字母
int test(int a = 2,int b = 3); Linux下修饰的函数名为_Z4testii
在centos7.6的环境下
VS
C 看函数名
Linux 只看函数名
int add ( int a,int b );
vs
2.4.7.2.2 修饰的函数名寻址时的使用
1生成符号表
在含有函数定义的.s文件汇编生成二进制的.o文件时,会生成一个修饰好的函数名与函数地址一一对应的符号表。
2查表的地址
在各.o文件进行合并时,会依据占位的函数修饰名通过符号表进行函数地址的补全。