【C++】函数重载的形式及其背后原理 您所在的位置:网站首页 函数的几种表达形式是什么样的 【C++】函数重载的形式及其背后原理

【C++】函数重载的形式及其背后原理

2024-07-12 12:23| 来源: 网络整理| 查看: 265

常言道:中国有俩球,谁都赢不了!这句话在不同的语境下有不同的意思

C++中,函数支持在同一作用域下声明几个功能类似的同名函数,但需要遵守以下规定

形参个数不同形参类型不同形参类型的顺序不同

编译器会在调用这些同名函数的时候,根据具体情况来选择不同的函数

感谢你关注慕雪,欢迎来我的寒舍坐坐❄慕雪的寒舍

文章目录 1.函数重载的样式1.1形参类型不同1.2形参个数不同1.3形参类型顺序不同1.4非函数重载 2.C++实现函数重载的原理2.1编译生成可执行文件2.2查看汇编2.2.1汇编函数名的含义2.2.2查看C语言汇编 2.3得出结论 3.语法extern"C"3.1C++调用C语言静态库3.2用条件编译解决问题3.3C语言调用C++的库 4自己整一个静态库4.1C++调用C语言静态库4.2C语言调用C++静态库勘误,上述结论错误 结语

1.函数重载的样式

上面提到了函数重载的3个规定,下面让我们来用具体示例认识一下它们

假设我们需要一个A+B的代码,如果每次都需要根据不同数据类型来写不同的函数去实现这个功能,未免有点太过繁杂。

在C++中,只需要修改函数的参数,即构成了函数重载,编译器就会自己选择对应的函数进行相加操作

1.1形参类型不同 //函数重载 int Add(int a, int rb){ return a + b; } long Add(long a, long b){ return a + b; } double Add(double a, double b){ return a + b; } int main() { cout return a + b + c; }

image-20220429185334477

1.3形参类型顺序不同

这里的顺序并不是a和b的顺序哈!只把a和b换一个位置是不构成函数重载的

这里指的是先传int再传double,和先传double再传int的两种函数

//形参类型的顺序不同 void Add(int a, double b) { cout return a+b; } double Add(double a,double b){ return a+b; } //test.cpp #include "Add.h" int main() { cout f(3,4); return 0; }

编译程序后,执行objdump -S,可以看到f函数被命名为_Z1fii,代表函数名长度为1,原本函数名f,和函数参数(int ,int)

image-20220429195810955

现在我们知道了汇编中这个函数名的命名规则,那它和C++支持函数重载有什么关系呢?

在这之前,我们还需看看c语言程序,汇编代码中函数又是怎么命名的

2.2.2查看C语言汇编

这里我把之前的函数修改成了C语言的样式,gcc编译后再来看看它的汇编

void f(int a,int b){ printf("%d %d\n",a,b); }

image-20220429200244080

然后你就会发现,C语言汇编代码中的函数名,就是函数原本的名字f,没有添加任何东西!

image-20220429200356998

2.3得出结论

看到这里,你能猜出来为什么C++支持函数汇编,而C语言不支持了吗?

没错!那是因为C++的汇编代码中,函数名还保存了函数的形参类型,而C语言中并没有保存,自然无法区分两个函数

这个汇编函数名的命名方式也能解释C++函数重载的3种样式

假设我们有一个fun函数,那么我们可以推断出它的汇编函数名

类型形式一形式二形参个数不同_Z3funii(int,int)_Z3funiii(int,int,int)形参类型不同_Z3funii(int,int)_Z3fundd(double,double)形参类型顺序不同_Z3funid(int,double)_Z3fundi(double,int)

同时也能解释为何只修改函数返回值类型是不构成重载的,因为汇编代码中没有保存函数的返回值

正因为C++在汇编处理中能够以这种命名方式来区分同名的不同函数,并给它们赋予不同的地址,编译器在链接符号表的时候,才能通过函数传参的不同找到它需要调用的对应函数的地址

image-20220429202120839

在main函数的汇编中,也能找到对应函数的调用操作

image-20220429202215847

3.语法extern"C"

因为C++汇编处理中对函数名的修饰和C语言不同,所以C++中有这么一个语法,专门用来告诉编译器,某某某函数要用C语言的规则来修饰

#include using namespace std; extern "C" int fun(int a,int b); int main() { int sum=fun(1,2); return 0; } int fun(int a,int b){ return a+b; }

可以看到,使用这种方式修饰的fun函数,在汇编中就只有函数名,而不是C++形式原本的_Z3funii

image-20220429210808692

这样C语言的代码就可以链接这种方式写的C++静态库(前提是这个静态库中没有函数重载和C++的语法)

然后我就想问:这和C的静态库有啥区别……

当然有了!一个库里面有很多很多代码,总有些函数接口是C语言也能支持的嘛,这些接口就用C语言的方式来修饰,这样C语言也能调用了,不一举两得?

3.1C++调用C语言静态库

除了更改修饰方式外,extern"C"还用于让C++程序来调用C语言写的库

比如树莓派要用到的wiringPi库,它是用C语言实现的,在编程为静态库后,里面汇编对函数的修饰就固定了,并没有C++下的_Z1...和参数类型修饰。

这时候如果用C++直接来调用这个函数,C++程序是找不到对应的函数的。在这种情况下,extern"C"的作用就是让编译器以C语言的方式去寻找对应函数

比如下图的代码,调用了wiringPi库里面的初始化函数,是最常用的一个函数

image-20220429215242153

我们用G++编译器编译这个代码,就会发现,欸tnnd怎么没有报错啊?

image-20220429215353327

QQ图片20220419102802

其实吧,库函数的开发者早就想到了这一点。在平日编程中,也有办法来解决这个问题——那就是用条件编译指令!

3.2用条件编译解决问题

前情提要:在C++的编译环境中有一个预定义符号__cplusplus

cout int a = 0; } using namespace muxue;

image-20220429231603592

现在可以确认我们的结论,只有不包含任何C++的语法和函数重载的C++静态库,才能正常被C语言项目调用!

勘误,上述结论错误

22-05-06,在同学的提示下,发现了这个错误

之前C调用C++的方式有问题,因为我是直接把C++的语法放到了头文件中,在展开的时候C程序编译会报错

但如果把C++的语法放入cpp文件,头文件中不包含的话,就不会报错了!

image-20220506152800090

可以看到在最后的测试项目中,C语言程序成功调用了c++的语法并正确输出了内容

image-20220506152923834

这也能解释我关于谷歌TCmalloc库的疑惑了,看来C和C++真的是互通有无啊!

结语

本篇笔记详细解释了C++中函数重载的类型,以及背后的实现原理。

这个博客花了我整整4小时的时间,感觉很充实!

所以求个赞不过分吧!谢谢大家!

QQ图片20220424132540



【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有