C++ -函数重载-详解

发布于:2024-10-12 ⋅ 阅读:(7) ⋅ 点赞:(0)

博客主页:【夜泉_ly
本文专栏:【C++
欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

1.是什么

如果在百度中搜索重载这个词,会得到以下结果:
在这里插入图片描述

不怎么容易理解吧🤣。
其实,可以简单的把重载理解为同名,而函数重载就是定义同名函数
例如:

void Add(int a, int b);
void Add(double a, double b);

这在C语言中肯定会报错,但C++支持这样的操作,并且在调用时会根据传入的参数类型自动匹配对应的函数。

2.怎么用

函数重载不能支持所有同名函数,首先,参数列表需要不同,主要分三方面:

  • 参数个数不同
  • 参数类型不同
  • 类型顺序不同

这意味着,如果函数名相同、参数列表相同、但是返回值不同不构成重载!例如:

int func(int a, int b);
double func(int a, int b);

2.1示例

void func();	 //无参数
void func(int a);//单参数

构成重载——参数个数不同。


void func(int a, int b);//参数顺序不同
void func(int b, int a);//参数顺序不同

有歧义,不构成重载。
报错信息如下:
在这里插入图片描述


void func(int  a, int b);//第一个参数为整数
void func(char a,int b);//第一个参数为字符

构成重载——参数类型不同。


void func(int a, char b);//第一个参数为整数
void func(char a,int b);//第一个参数为字符

构成重载——类型顺序不同。


这时有人想到了缺省参数💡,于是出现了这种比较特殊的情况:

void func();
void func(int a = 0);

是否构成重载?
构成重载!符合重载函数的定义。
在这里插入图片描述
可以在上图的右下角看见,程序正常退出。

那么这样写有没有问题?
有问题!如果写出下面这句代码,则会引发歧义:

func();

在这里插入图片描述

3.原理

这里有两个问题:

  • 为什么C语言不支持重载,C++支持重载?
  • C++是怎么支持重载的?

想要解决这些问题,需要了解编译链接过程,以及函数名修饰规则

自动识别类型-函数重载。

3.1C/C++编译链接过程

这里只是简单提两句,因为更详细的内容我也不知道:

  1. 预处理:
    头文件展开 / 宏替换 / 条件编译 / 去掉注释……(.h/.c/.cpp文件变为.i文件)

  2. 编译:
    检查语法 / 生成汇编代码(.i文件变为.s文件)

    上面那个缺省参数的特殊情况在编译时不会报错,就是因为语法没错。

  3. 汇编:
    将汇编代码转化为二进制的机器码(.s文件变为.o文件)

  4. 链接:
    生成符号表 / 将目标文件(.o–>object)链接成可执行程序(.exe/a.out

3.2函数名修饰规则

C++ 编译器在编译过程中会对函数名进行修饰,以便区分不同的重载函数。修饰规则通常包括以下内容:

  • 参数类型:函数参数的类型信息。
  • 参数数量:函数参数的数量。
  • 返回类型:函数的返回类型信息。

在不同环境下,函数名修饰规则也不同,但目的都是将同名变为不同名。

3.3过程

二进制机器码与汇编代码是相互转换的关系,也就是说,如果想查看程序的底层实现,可以通过查看汇编代码来获取更直观的信息。
我在这里又建了三个文件:
func.h:

#pragma once
#include <stdio.h>
void func(int a, int b);
void func(double a, double b);

func.c:

#include "func.h"
void func(int a, int b)
{
	printf("void func(int a, int b)\n");
}
void func(double a, double b)
{
	printf("void func(double a, double b)\n");
}

test.c:

#include "func.h"
int main()
{
	func(1, 2);
	func(1.0, 2.0);
	return 0;
}

1.调用函数的过程

在开始调试的时候转到反汇编,会看见:
在这里插入图片描述
这里可以看见,在每个函数调用语句下都有一条“call指令+地址”:
在这里插入图片描述
这里重点关注第一个call指令,在调试到那里时,会跳转到jump指令,而这个jump指令的地址就是刚刚call指令后跟的地址:
在这里插入图片描述
而这个jump指令,会根据它后面跟的地址,真正跳转到函数的位置:
在这里插入图片描述

2.编译阶段的函数调用

在编译过程中,函数调用会被转化为 call 指令,后面跟着函数名(函数的地址),类似刚刚看见的:
在这里插入图片描述
当函数的定义和声明分离时,编译阶段无法立即获取函数的实际地址,因为此时只包含了(.h),即编译器只看到声明,而函数的定义在其他文件中。
在这种情况下,编译器能通过声明完成编译,但不会生成最终的地址。(我只会用VS2022,而且不知道怎么调出这种效果😅)
在链接阶段,链接器才会将调用语句与实际的函数定义关联起来,找到函数的真正地址。

编译错误与链接错误:

  • 因为编译器在看到声明时只知道函数存在,但没有具体的实现细节,因此只要声明正确,编译可以顺利通过。
  • 但是,链接阶段如果找不到函数的定义,就会发生链接错误。

当然,当函数的定义与声明在同一个源文件中时,编译器可以直接找到函数的地址,因为定义已经存在,编译器能够在编译阶段就处理地址问题。


回到函数重载:
编译阶段,C++编译器会通过函数名修饰为每个重载函数生成唯一的符号,从而确保编译器能够区分同名但参数列表不同的重载函数。
而C语言不会进行函数名修饰,因此遇到同名函数会编译失败。
这就解释了为什么C语言不支持重载,C++支持重载?和C++是怎么支持重载的?

总结

函数重载是 C++ 中一项重要的特性,它提高了代码的灵活性和可读性。
通过函数名修饰规则,C++ 在编译过程中实现了自动类型匹配,避免了调用时的歧义。
在实际编程中,合理使用函数重载可以极大地提高编程效率和代码质量。

在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!
本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!