在上一篇推文中我们讨论了C代码与C++代码相互引用的问题,还没看过文章的可以点这里《C与C++代码的相互引用》。文章中关于报错“无法解析的外部符号”这一问题得到的答案是:C和C++代码在编译成目标文件时对函数的命名不同。今天讨论下为何会导致函数名不同。
还是上次的工程,工程结构如下:
我们已经知道了两种类型文件的同一函数生成的目标文件的不同,如下两图分别为”Main.cpp”和“Main.c”生成的两个目标文件
由上图可知对于同一个函数“PrintA()”,在cpp格式下生成目标文件时函数为“PrintA@@YAXXZ”,在.c格式下生成函数为“_PrintA”。查了一些资料了解到,cpp文件在编译时会将函数的参数列表一起编译成新的函数名,究其原因,就是今天要讨论的问题:函数重载。
函数重载:C++允许在同一范围中声明几个功能类似的同名函数,但是这些同名函数的形式参数(指参数的个数、类型或者顺序)必须不同,也就是说用同一个函数完成不同的功能(以上摘自百度百科)。
关于重载需满足两个关键点(并非C++特有):
1.函数名相同
2.形参不同(满足个数,类型,顺序中至少一个不同)
C语言不支持函数重载,为了实现相似功能,我们需要给函数定义不同的名字。而在C++中则允许使用相同的函数名实现相似功能,但为了达到这一目的,在代码编译时就必须连同参数列表一同编译,从而区分多个重载函数。
下面看一个简单的例子,我们将之前的工程简单改写下,所有源文件改为cpp格式,代码如下:
Main.cpp
#include "Function.h"
int main()
{
PrintA(5.2);
PrintA((float)2.6);
PrintA(5);
PrintA('f');
return 1;
}
Function.h
#pragma once
int PrintA(double parm);
float PrintA(float parm);
void PrintA(int parm);
void PrintA(char parm);
Function.cpp
#include <stdio.h>
int PrintA(double parm)
{
printf("type double=%f\n",parm);
return 1;
}
float PrintA(float parm)
{
printf("type float=%f\n",parm);
return (float)1.0;
}
void PrintA(int parm)
{
printf("type int=%d\n",parm);
}
void PrintA(char parm)
{
printf("type char=%c\n",parm);
}
编译运行结果如下:
主函数中调用四个不同的函数,但函数名相同,即“PrintA()”。根据“Function.h”中函数声明可知四个函数的区别在于四个函数参数列表及返回值类型不相同,在“Function.cpp”中有对应的函数定义。函数功能比较简单,是将传入的参数打印到控制台,如上图所示。
这四个同名函数即构成重载函数,若要实现这样的功能,在C语言中不被允许,不论参数是否相同,都必须保证函数名不同,我们进一步分析一下Function.cpp的目标文件
四个函数名转换后分别为:
“PrintA@@YAHN@Z”,
“PrintA@@YAMM@Z”,
“PrintA@@YAXH@Z”,
“PrintA@@YAXD@Z”
由此可知函数在编译时的命名规则为函数名@@YA返回值类型,参数表类型@Z,只针对当前编译环境(VS2019)。其中返回值类型和参数类型均由字母表示,根据这个程序分析得出字母含义:
H:int
N:double
M:float
X:void
D:char
关于重载在C++中是比较常用的,在其他的面向对象的语言中也有相似定义,有关重载函数的意义:
1.为了方便调用,程序员只需要知道一个函数名,而不必关心调用时的参数类型。例如C语言中如果要实现输出不同类型的函数,就必须规定不同的函数名,在调用时必须记住所有函数名并传入对应类型的参数,而在本程序中则可以忽略此问题,直接调用即可。
2.对于类的构造函数必须与类名相同,在C++引入类的概念后为了实现多种构造函数则必须使用函数重载功能。