您现在的位置是:首页 >技术交流 >【C++】函数重载及实现原理网站首页技术交流

【C++】函数重载及实现原理

林 子 2023-04-28 22:30:03
简介【C++】函数重载及实现原理

       🔥🔥 欢迎来到小林的博客!!
      🛰️博客主页:✈️林 子
      🛰️博客专栏:✈️ 小林C++之路
      🛰️社区 :✈️ 进步学堂
      🛰️欢迎关注:👍点赞🙌收藏✍️留言

什么是函数重载?

函数重载就是允许出现多个同名函数,但是参数类型,参数顺序,参数数量不同才能构成函数重载。

我们先来看看以下这段代码,用C语言编译会怎么样。

#include<stdio.h>
void fun(int i)
{
	printf("fun(int i )
");
}

void fun(int i, double d)
{
	printf("fun(int i, double d)
");
}

int main()
{
	fun(1);c
	fun(1, 1.1);
}

我们会发现出现这样的错误。

在这里插入图片描述

这是因为C语言再检测时发现了同名函数。那如果我们把文件改成cpp形式再编译呢?

我们会发现cpp可以打印出来。

在这里插入图片描述

这是因为C++支持函数重载,而函数重载就是可以同时存在多个函数名相同的函数,但是必须至少满足以下三点的其中一点。

1. 函数参数数量不同

2. 函数参数类型不同

3. 函数参数顺序不同

例如:

//以下两个fun函数数量,类型不同
void fun(int i)
{
	printf("fun(int i )
");
}
void fun(int i, double d)
{
	printf("fun(int i, double d)
");
}

//以下两个a函数,数量相同,但是参数顺序不同..也可以归纳为类型不同
void a(int i, double d)
{
	;
}
void a(double d, int i)
{
	;
}
//以下两个b函数,参数类型不同
void b(int i) {
	;
}
void b(double d)
{
	;
}

为什么要有函数重载?

我们知道了函数重载是什么之后,那么为什么要有函数重载?比如我要写两个函数,一个函数可以对整形进行加法运算,而另一个函数要对浮点型进行加法运算。那么我们在C语言中只能这样写。

#include<stdio.h>
int addI(int x, int y)
{
	return x + y;
}
double addD(double x, double y)
{
	return x + y;
}

int main()
{
	addI(10,20);
	addD(10.5, 20.5);
}

但是在C++中,支持函数重载,我们可以这样写。

#include<stdio.h>
int add(int x, int y)
{
	return x + y;
}
double add(double x, double y)
{
	return x + y;
}

int main()
{
	add(10,20);
	add(10.5, 20.5);
}

有没有发现支持了函数重载之后,add 有自动类型推导 的作用? 这就是函数重载的作用。避免对各种各样功能相同,但是类型不同的函数取不同的函数名。这样记起来也麻烦,用了函数重载之后,那么只需要记住一个函数即可。

函数重载的原理

那么函数重载是如何实现的呢?那我们要知道.c/cpp 源程序在生成可执行程序时要经历的四个阶段。

1.预处理 : 负责头文件展开,宏替换,条件编译,去掉注释等…

2. 编译 : 检查语法,语法没有错误后,把C代码转换为汇编指令

3. 汇编 : 将汇编指令转换为二进制机器码

4.链接 : 合并符号表

那么我们就在每个阶段来观察一下这个函数,接下来将会在linux系统下演示。

首先,我们先写一个fun.c的源文件

#include<stdio.h>

void fun(int i)
{
  printf("fun(int i )
");
}

void fun(int i ,double d)
{
  printf("fun (int i , double d)
");
}

int main()
{
  fun(1);
  fun(1,1.1);
  return 0;
}

然后执行命令 gcc -E fun.c -o fun.i 将fun.c进行预处理后生成一个.i文件

然后我们发现编译器并没有报错,那么说明冲突不是在预处理阶段产生的。我们来看看fun.i的内容。

在这里插入图片描述

我们会发现,和C语言代码并没有其他区别,区别就是头文件展开了。

接下来,我们再执行 gcc -S fun.i -o fun.s 将预处理过的.i文件进行编译,转换为汇编代码。

在这里插入图片描述

然后我们就会发现编译出错,那么说明冲突是编译时产生的。那么我们换成.cpp文件来看。

我们把fun.c 改成funcpp.cpp, 然后分别执行 g++ -E funcpp.cpp -o funcpp.ig++ -S funcpp.i-o funcpp.s

在这里插入图片描述

我们会发现编译没有问题,那么我们来查看一下汇编代码。

image-20230425104245124

我们发现 fun(int i)函数实际修饰名是_Z3funi。

那么我们再看看fun(int i ,double d)的函数名是什么。

在这里插入图片描述

fun(int i ,double d)的实际修饰名是 _Z3funid。

在Linux操作系统下, 这个3表示函数名的字符串长度,后面跟上函数名,之后就是参数类型,int 为i,double为d。

也就是说虽然 fun(int i ,double d)fun(int i ,double d)函数名相同,但是在修饰之后。实际上的函数名是_Z3funi_Z3funid。所以并不存在冲突问题。

那么为什么C语言不行呢?那么我们写一个正确的C语言代码,重复上面步骤,看看C语言的实际函数名是什么。

这是一段正确的C语言代码。

#include<stdio.h>

void fun(int i)
{
  printf("fun(int i )
");
}

int main()
{
  fun(1);
  return 0;
}

随后进行预处理和编译。

编译后我们查看fun.s文件,会发现函数fun实际上就是函数fun。没有其他修饰规则,所以就造成了函数名冲突问题。

在这里插入图片描述

总结

函数重载本质就是对函数名进行了修饰,允许函数名相同,但是参数数量,顺序,类型不同的函数同时共存。虽然表面上看起来这个函数名是这个函数的函数名,但是在编译后函数名会被处理修饰。所以每个重载的函数真正意义上的函数名是不同的。而C语言则没有加修饰,函数名就是函数名,所以就会出现冲突编译报错的情况。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。