当前位置:首页 » 《我的小黑屋》 » 正文

C++ -函数重载-详解

7 人参与  2024年10月02日 18:41  分类 : 《我的小黑屋》  评论

点击全文阅读


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

在这里插入图片描述

C++ -函数重载-详解

1.是什么2.怎么用2.1示例 3.原理3.1C/C++编译链接过程3.2函数名修饰规则3.3过程1.调用函数的过程2.编译阶段的函数调用 总结

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++编译链接过程

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

预处理:
头文件展开 / 宏替换 / 条件编译 / 去掉注释……(.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.调用函数的过程

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

2.编译阶段的函数调用

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

编译错误与链接错误:

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

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


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

总结

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

在这里插入图片描述


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


点击全文阅读


本文链接:http://zhangshiyu.com/post/167266.html

<< 上一篇 下一篇 >>

  • 评论(0)
  • 赞助本站

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

关于我们 | 我要投稿 | 免责申明

Copyright © 2020-2022 ZhangShiYu.com Rights Reserved.豫ICP备2022013469号-1