C++ -函数重载-详解

夜泉_ly 2024-10-12 08:05:03 阅读 66

博客主页:【夜泉_ly】

本文专栏:【C++】

欢迎点赞👍收藏⭐关注❤️

在这里插入图片描述

C++ -函数重载-详解

1.是什么2.怎么用2.1示例

3.原理3.1C/C++编译链接过程3.2函数名修饰规则3.3过程1.调用函数的过程2.编译阶段的函数调用

总结

1.是什么

如果在百度中搜索重载这个词,会得到以下结果:

在这里插入图片描述

不怎么容易理解吧🤣。

其实,可以简单的把重载理解为同名,而函数重载就是定义同名函数。

例如:

<code>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);//参数顺序不同

有歧义,不构成重载。

报错信息如下:

在这里插入图片描述


<code>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);

是否构成重载?

构成重载!符合重载函数的定义。

在这里插入图片描述

可以在上图的右下角看见,程序正常退出。

那么这样写有没有问题?

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

<code>func();

在这里插入图片描述

3.原理

这里有两个问题:

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

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

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

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

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

预处理:

头文件展开 / 宏替换 / 条件编译 / 去掉注释……(<code>.h/.c/.cpp文件变为.i文件)

编译:

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

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

汇编:

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

链接:

生成符号表 / 将目标文件(.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.调用函数的过程

在开始调试的时候转到反汇编,会看见:

在这里插入图片描述

这里可以看见,在每个函数调用语句下都有一条“<code>call指令+地址”:

在这里插入图片描述

这里重点关注第一个<code>call指令,在调试到那里时,会跳转到jump指令,而这个jump指令的地址就是刚刚call指令后跟的地址:

在这里插入图片描述

而这个<code>jump指令,会根据它后面跟的地址,真正跳转到函数的位置:

在这里插入图片描述

2.编译阶段的函数调用

在编译过程中,函数调用会被转化为 <code>call 指令,后面跟着函数名(函数的地址),类似刚刚看见的:

在这里插入图片描述

函数的定义和声明分离时,编译阶段无法立即获取函数的实际地址,因为此时只包含了(.h),即编译器只看到声明,而函数的定义在其他文件中。

在这种情况下,编译器能通过声明完成编译,但不会生成最终的地址。(我只会用VS2022,而且不知道怎么调出这种效果😅)

在链接阶段,链接器才会将调用语句与实际的函数定义关联起来,找到函数的真正地址。

编译错误与链接错误:

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

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


回到函数重载:

编译阶段,C++编译器会通过函数名修饰为每个重载函数生成唯一的符号,从而确保编译器能够区分同名但参数列表不同的重载函数。

而C语言不会进行函数名修饰,因此遇到同名函数会编译失败。

这就解释了为什么C语言不支持重载,C++支持重载?和C++是怎么支持重载的?

总结

函数重载是 C++ 中一项重要的特性,它提高了代码的灵活性和可读性。

通过函数名修饰规则,C++ 在编译过程中实现了自动类型匹配,避免了调用时的歧义。

在实际编程中,合理使用函数重载可以极大地提高编程效率和代码质量。

在这里插入图片描述


希望本篇文章对你有所帮助!并激发你进一步探索编程的兴趣!

本人仅是个C语言初学者,如果你有任何疑问或建议,欢迎随时留言讨论!让我们一起学习,共同进步!



声明

本文内容仅代表作者观点,或转载于其他网站,本站不以此文作为商业用途
如有涉及侵权,请联系本站进行删除
转载本站原创文章,请注明来源及作者。