栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C++是如何支持函数重载的?

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C++是如何支持函数重载的?

hello,大家好呀!经过上一篇博客的学习,我们已经搞懂了为什么C++编程时几乎都要敲上一句using namespace std;今天我们来拿捏C++语法的另一个小细节:C++是如何支持函数重载的?

文章目录
  • 1.C++输入&输出
  • 2.缺省参数
  • *3.函数重载
    • 3.1函数重载概念
    • 3.2名字修饰(name Mangling)
      • 3.2.1编译链接的过程
      • 3.2.2采用C++编译器编译后结果
      • 3.2.3采用C语言编译器编译后结果
    • 3.3 extern “C”
      • 3.3.1 问题描述
      • 3.3.2 解决方法
        • 3.3.2.1环境配置
        • 3.3.2.2C++调用C
        • 3.3.2.2C调用C++
  • 4.总结

1.C++输入&输出


大家看,效果和C语言的输入输出是一样的,而且很形象。
cin >> a;向cin输入的内容流到a、b里面保存起来。
cout << a;像a、b里面的内容流入cout中。
endl大家可以先理解为一个变量其功能和’n’相同,表示换行。
说明:

  1. 使用cout标准输出(控制台)和cin标准输入(键盘)时,必须包含< iostream >头文件以及std标准命名空
    间。
  2. 使用C++输入输出更方便,不需增加数据格式控制,比如:整形–%d,字符–%c
  3. cout(ostream)、cin(istream)在C++中是对象,>>(流提取运算符)、<<(流插入运算符)其实是运算符重载,这个大家要学了类和对象才能理解,今天姑且先记住它们的用法,以后再详细讲解。
  4. 如果想控制格式输出,建议用printf。C++也能控制,感兴趣的同学可以去网上搜一搜(讲道理没必要)。
2.缺省参数

缺省参数是声明或定义函数时为函数的参数指定一个默认值。在调用该函数时,如果没有指定实参则采用该默认值,否则使用指定的实参。

缺省参数也叫默认参数,可分为两类:

  1. 全缺省参数

    传参只能顺序传,不能用这种TestFunc(,8);方式只想给b传参。
  2. 半缺省参数

因为传参是从左往右传的(大家不要跟底层的参数压栈搞混了,参数从右往左压栈还是从左往右压栈取决于编译器,VS2019就是从右往左压栈),所以半缺省参数必须从右往左缺省,并且是连续的。

注意:缺省参数不能在函数声明和定义中同时出现,如果声明与定义位置同时出现,恰巧两个位置提供的值不同,那编译器就无法确定到底该用那个缺省值。

但是可以声明给缺省参数,定义不给。

声明不给缺省参数,定义给则不行,编译器会报错。因为编译阶段编译器不知道你写的函数是带缺省参数。

*3.函数重载 3.1函数重载概念

C语言不支持同名函数,但是C++支持在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 顺序)必须不同,与返回类型、是否为缺省参数无关,常用来处理实现功能类似数据类型不同的问题。


3.2名字修饰(name Mangling)

为什么C++支持函数重载,而C语言不支持函数重载呢?
在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
我们用Linux来演示,不用VS2019,因为VS2019不方便看底层的一些东西。

3.2.1编译链接的过程


预处理阶段:f.cpp会有函数的声明和定义,test.cpp有函数声明和调用。

在编译、汇编之后会产生一个符号表:记录了函数名和函数地址的映射以及其他东西。这里只说一部分。

main函数call函数一定call的是函数的地址,那么问题来了,main函数只有函数的声明,没有函数的定义,那它是如何找到函数的地址,调用函数的呢?
其实编译器会拿函数名去f.o的符号表里找函数的地址填上,想一想,这是不是就是链接呀。
现在大家知道为什么C语言不支持函数重载了吗?
因为C语言的函数名同名且没有进行修饰,编译阶段就会因为命名冲突报错。
C++对同名函数进行了修饰。

3.2.2采用C++编译器编译后结果


结论:在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。

函数名修饰:_Z+函数名长度+函数名+参数类型首字母

3.2.3采用C语言编译器编译后结果


结论:在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。

3.3 extern “C” 3.3.1 问题描述

其实链接的时候不止做了通过函数名从符号表找函数地址填上这类事情,我还可以用其他第三方库里面的东西,分为静态库和动态库。
那么问题又来了:
如果我是一个C的程序,而库是C++的,那么我们能否调用呢?
如果我是一个C++的程序,而库是C的,那么我们能否调用呢?
ok,这些都是可以的。

虽然C、C++可以交叉调用,但是两者之间存在着一个鸿沟:因为C++的函数名是修饰过的,C语言没有,那么C就没法调用C++,而C++也无法去找C库里的函数。当然,这个问题也可以解决。

3.3.2 解决方法 3.3.2.1环境配置

假设我写了一个栈的静态库(新建一个工程改静态库)

在此路径下就产生了一个Stack_C.lib的文件

而我有一个C++的程序想解决括号匹配问题需要用到Stack_C.lib这个静态库,怎么办呢?
用相对路径包含一下头文件就好了(这一步需要你自己找到Stack.h的文件路径,…/表示当前目录的上一级)。

此时我们来编译一下:

哎?怎么报了一堆链接错误呢?咋回事呢?
还得配置一下链接器:


3.3.2.2C++调用C

extern "C"告诉编译器,声明的函数是C库,要用C的方式去链接调用即直接用不经修饰的函数名去找

3.3.2.2C调用C++

按照同样的方法我们新建一个过程,生成Stack_CPP.lib的文件。

然后重新添加一下Stack_CPP的附加库目录和附加依赖项,方法和上面相同。

还是报了一堆链接错误,为什么这个时候也链接不上呢?
因为C语言的函数名没有修饰过,而C++的符号表里函数名是修饰过的。
那咋办呀?条件编译!
我教大家两种方法:

  1. 我们在Stack_CPP的头文件加上如下图所示的条件编译。
    __cplusplus是C++才有的宏标识,意思在C++文件里我要用C的方式去修饰函数名,而头文件在C文件里展开后,因为没有__cplusplus这个宏标识,EXTERN_C就表示空白(因为C语言识别不了extern “c”,放出来会报错),啥也不会做。这样就完美解决了这个问题。


运行结果:

  1. 还有一种简化写法:条件编译括起来多个声明

    运行结果:
4.总结

还是那句老话,学习C++切勿急于求成,戒骄戒躁,宁静致远。你们猜博主写这篇博客写了多久?因为博主学习时别人用的是VS2013,而我用的是VS2019,在设置附加库目录和附加依赖项出了很多问题,反正博主写完这篇博客,心态已经发生了质的改变,没有了那种世俗的欲望。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/849252.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号