《C++初阶之路》函数重载
一、本章重点
- 什么是函数重载?
- 函数重载的条件
- 为什么有函数重载?
- 为什么C不支持函数重载,C++确能支持函数重载?
- extern “C”
二、函数重载
2.1函数重载的概念
简单来说,C++允许同一作用域中出现函数名相同,参数不同,功能相似的函数,而这些函数就构成函数重载。
void Swap(int* a, int* b) {int temp = *a;*a = *b;*b = temp; }void Swap(double* a, double* b) {double temp = *a;*a = *b;*b = temp; } //这两个函数就构成函数重载
2.2函数重载的条件
第一要满足:函数名相同
第二要满足:参数不同,具体表现在参数类型的顺序、参数的个数、参数的类型。
参数的类型不同的函数重载
void Pt(int a) {cout << a << endl; }void Pt(double a) {cout << a << endl; } //类型不同
参数的个数不同的函数重载
int Add(int a, int b, int c) {return a + b + c; }int Add(int a, int b) {return a + b; } //个数不同
参数类型的顺序不同
void Func(int a, double b) {cout << a << b << endl; }void Func(double a, int b) {cout << a << b << endl; } //顺序不同
2.3为什么有函数重载?
就是问函数重载到底能干嘛?c语言不支持同一作用域存在同名函数,那么我们修改一下函数名不就行了吗?函数重载真正能体现价值的地方到底在哪呢?
第一:书写函数名方便。
比如,我们要写两个交换函数,第一个是交换两个整形,第二个是交换两个浮点型,那么你只需要都取Swap即可,不需要写成Swapi、Swapd,如果参数复杂呢?你又要写成什么呢?
到不如直接写成Swap,然后传不同的参数,编译器会自动匹配最符合的函数。
第二:类中构造函数的实现也依靠函数重载
构造函数是同名的成员函数,它一般被划分为:有参构造、无参构造、拷贝构造,它们构成函数重载。
第三:模板的底层实现也依靠函数重载
模板的其实就是让编译器就为你创建重载函数,比如Swap(const T& a,const T&b)
当你传两个整形给a和b时,编译器会自动生成Swap(int* a,int* b),当你传两个浮点型double时,,编译器会自动生成Swap(double* a,double* b)。
以上等等C++中好用的机制都依靠于函数重载,函数重载在C++中的地位不言而喻。
2.4为什么C不支持函数重载,C++确能支持函数重载?
首先,我们需要了解函数名修饰规则
函数名修饰(Decorated Name)就是编译器在编译期间创建的一个字符串。用来指明函数的定义或原型。
在编译期间,C++会对函数名做一些处理,这种处理与函数参数有关,而C语言并不会,或者说C语言的函数名修饰规则与函数中参数无关。
我们都知道生成一个可执行文件,分以下步骤:预处理、编译、汇编、链接。
预处理:头文件展开、去注释、条件编译、宏替换(在linux平台下,去注释先于宏替换)
编译:先进行语法检查,然后将高级语言写的代码转化为汇编指令。
汇编:汇编来源于汇编语言中专业词汇,指将汇编指令转为二进制代码。
链接:并不是简单的将多文件合并,主要进行的是文件之间的交互。
以Swap.h、Swap.c、Test.c为例。
Swap.h主要在Swap.c和Test.c中展开。
Swap.c在编译之后,链接的时候,会生成一个符号表,符号表的主要作用是映射函数名和函数地址,简单来说就是存储函数名和对应的函数地址,然后Test.c就以调用的函数名去取符号表中的函数名对应的地址,完成Swap.c和Test.c的交互。
图解:
这下我们可以开始解释为什么C不支持函数重载,C++确能支持函数重载?
因为C语言的函数名修饰规则与函数参数无关,或者说没有修饰规则。
而C++在编译期间,会将函数名修饰一下,即将函数名与参数联系在一起。
图解:
这里我们用math.h、math.c、test.c来举例
Test.cpp调用函数Swap(int a,int b)编译之后,它的函数名会变成_ZSwapii。
同理调用函数Swap(int a,double b) 编译之后,它的函数名会变成_ZSwapid。
(以上的函数名变化参考至linux平台,具体函数名修饰规则不同平台不一样)
然后在链接的时候去符号表找函数的地址,再将函数的地址填在Test.cpp中调用的地方。
总之就是,我们不想改的函数名,编译器帮我们改了,这就是函数重载的底层原理。
这里同样可以解释为什么返回值不构成函数重载,因为c++的函数名修饰规则不考虑返回值,只将函数名与参数结合起来组成新的函数名。
而C语言就很老实,你写什么函数名,我是不会改的,如果在同一作用域存在同名函数,在编译期间会发生错误,因为语法不允许。
以下这个.c文件很好说明这一点
2.5 extern “C”
C++为了调用C语言写的库,增加了extern “C”这样的语法,以前C语言中extern的作用一般是声明外部符号,而这里extern的作用也差不多,都是其声明告知的作用。
extern “C”一般的用法有两种:
第一:添加在函数声明的前面
第二:extern “C”再用花括号将函数声明括起来。
其作用是告知编译器该函数用C语言去编译。
经过上面的函数名修饰规则,我们知道C语言编译的库是没有改函数名的,而C++调用时会修改函数名(把函数参数与函数名结合),当用C++直接C库,那个在链接时,一个修改过的函数名,去符号表里找没被修改过的函数,显然是找不到的,这时会产生链接错误。
举例:C语言编译含有int Add(int a,int b)的库时,C++调用这一库,如果没有加上extern “C”那么在链接的时候,C++会以_ZAddii去符号表找_ZAddii的地址,但是符号表只有Add,找不到,导致连接错误。
如果加上extern “C”那么C++会以C语言的方式去编译Add(),链接的时候,C++会以Add去符号表找Add的函数地址,此时自然能找到。
包含extern “C”自然就能够使C++调用C语言的库。
现在我们知道了:c能够调用c的库,c++能够调用c++的库,c++也能调用c的库(加上extern “C”),那么c语言能调用C++库吗?
答案是确定的,不过c语言调用c++的库不是那么容易,我们需要在C++的库中运用extern “C”,即用extern “C”将C++中的头文件中的函数声明括起来,然后生成c++的库,c语言再去调用这个生成的库,但c语言包含的c++的头文件中有extern “C”,c语言编译器识别不了,因此需要用到条件编译,将c语言包含c++头文件中的extern “C”去除。
具体模拟:
c++调用c静态库(VS2019)
第一步建库:
vs2019默认生成可执行程序,这里更改一下项目属性
将配置类型改为静态库
之后点击生成解决方案
打开所在文件上层目录的Debug可以看到成功生成Add_C.lib
再使用C++调用这个生成的静态库
新建测试的项目
包含Add.h,这里用的是相对路径。
之后再配置一下
接下来运行一下
链接失败,因为c代码的函数名编译时不会与参数结合,而c++代码编译时会将函数名与参数结合,导致链接的时候找不到。
用extern “C”包含Add.h即可,意思是告诉C++编译器,编译时用C的方式解析改函数,即使用C的函数修饰规则。
c调用c++静态库(VS2019)
这里演示就快点了
把.c改为.cpp
由于c项目中要包含Add.h
其中的extern “C”编译器是不认识的
因此要保证Add.h头文件中extern “C”在c++的静态库存在, 在c项目包含该头文件时不存在。
可以用条件编译(__cplusplus是c++特定的标识符,c没有)
因此改为:
然后C去调用这个C++的静态库
总结:
c++调用c库较容易,只需要在c++中使用extren “C”即可
而c调用c++库,确需要改c++库,如果有现成的c++库,确需要找到头文件加上extren “C”,改动源码,改变之后C++还能再调用这个C++库吗?显然不行了。
《C++初阶之路》函数重载相关推荐
- 《C++初阶之路》inline函数
一.本章重点 为什么要有inline函数(内联函数)? 什么是内联函数? 为啥所有函数不都改成内联? 内联函数的使用场景 内联函数注意事项 二.Inline函数 2.1为什么要有inline函数(内联 ...
- 《C++初阶之路》命名空间的意义
一.本章重点 为什么有命名空间? 什么是命名空间? 命名空间的使用 二.为什么要存在命名空间? 我们需要知道的是:C++大多数语法的出现都是解决C语言的不足. 在C语言中,下面这样的代码能过通过编译吗 ...
- 【C++初阶】C++入门一(命名空间、输入输出、缺省参数、函数重载等)
文章目录 (1)前言 (2)C++关键字(C++98) (3)命名空间 1)命名空间的定义 2)命名空间的使用 (3)C++的输入&输出 (4)缺省参数 1)缺省参数的概念 2)缺省参数的分类 ...
- 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...
[初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...
- 趋势交易大师php,系统交易的初阶——趋势交易者路在何方?
系统交易的初阶--趋势交易者路在何方? (2010-02-02 21:09:11) 标签: 股票 系统交易的初阶--趋势交易者路在何方? 问:我算是看明白了,其实你的问题太好理解了,记得金融怪杰里面有 ...
- C++ --函数模板初阶的知识总结
一,泛型编程 编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础. 二,函数重载和函数模板的关联 函数重载:定义函数名相同而形参列表(形参个数或形参类别)不同的多个函数.. 函数模板 ...
- C++初阶 — vector
目录 一.vector的介绍及使用 1. vector的介绍 2. vector的使用 2.1 vector的定义 2.2 vector iterator 的使用 2.3 vector 空间增长问题 ...
- <初识C++(1)>《C++初阶》
目录 <初识C++><C++>之学习 --By 作者:新晓·故知 1. C++关键字(C++98) 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C++输入 ...
- C/C++内存管理模板初阶
内存管理和模板初阶 1 内存管理 1.1 C/C++ 的内存分布 1.2 C 中动态内存管理方式 1.3 C++ 中动态内存管理方式 1.3.1 new/delete操作内置类型 1.3.2 new/ ...
最新文章
- 研究人员研发可自我修复的“电子皮肤”,重点是还能回收再利用
- ElasticSearch6 查询模板的创建使用
- [转]ASP.Net缓存总结
- maven工程的标准目录结构
- Java并发编程笔记之LinkedBlockingQueue源码探究
- linux chrome 安装过程记录
- mongoose 批量修改字段_WordPress图片路径批量替换方法
- Redis夺命十二问,你能扛到第几问?
- 计组之数据运算:12、加法器设计
- 菜鸟进阶Linux高手之路——第三天
- ucenter词语过滤原理
- AE插件 点线面三维粒子插件 Plexus Mac v3.1.8破解版
- 网页三剑客的一些序列号
- 怎么使用biopython_什么是Biopython? 你能用Biopython做什么? Biopython功能概。
- word打开老是配置进度_打开word文档显示配置进度怎么办 Word文档提示配置进度解决办法...
- 服务器系统补丁失败,windows2008系统更新补丁,以及失败解决方法
- Win7打开文件安全警告怎么关闭
- 奇志思达-微网站前端规则建议
- 在德国搜索超模海蒂和小龙女有风险哦
- 【论文精读】KD-MVS