一、本章重点

  1. 什么是函数重载?
  2. 函数重载的条件
  3. 为什么有函数重载?
  4. 为什么C不支持函数重载,C++确能支持函数重载?
  5. 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++初阶之路》函数重载相关推荐

  1. 《C++初阶之路》inline函数

    一.本章重点 为什么要有inline函数(内联函数)? 什么是内联函数? 为啥所有函数不都改成内联? 内联函数的使用场景 内联函数注意事项 二.Inline函数 2.1为什么要有inline函数(内联 ...

  2. 《C++初阶之路》命名空间的意义

    一.本章重点 为什么有命名空间? 什么是命名空间? 命名空间的使用 二.为什么要存在命名空间? 我们需要知道的是:C++大多数语法的出现都是解决C语言的不足. 在C语言中,下面这样的代码能过通过编译吗 ...

  3. 【C++初阶】C++入门一(命名空间、输入输出、缺省参数、函数重载等)

    文章目录 (1)前言 (2)C++关键字(C++98) (3)命名空间 1)命名空间的定义 2)命名空间的使用 (3)C++的输入&输出 (4)缺省参数 1)缺省参数的概念 2)缺省参数的分类 ...

  4. 【初阶】unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流...

    [初阶]unity3d官方案例_太空射击SpacingShooter 学习笔记 显示分数时,如何让函数之间相互交流 一.关于 显示分数时,如何让函数之间相互交流 这是一个非常好的逻辑问题 1 思路:主 ...

  5. 趋势交易大师php,系统交易的初阶——趋势交易者路在何方?

    系统交易的初阶--趋势交易者路在何方? (2010-02-02 21:09:11) 标签: 股票 系统交易的初阶--趋势交易者路在何方? 问:我算是看明白了,其实你的问题太好理解了,记得金融怪杰里面有 ...

  6. C++ --函数模板初阶的知识总结

    一,泛型编程 编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础. 二,函数重载和函数模板的关联 函数重载:定义函数名相同而形参列表(形参个数或形参类别)不同的多个函数.. 函数模板 ...

  7. C++初阶 — vector

    目录 一.vector的介绍及使用 1. vector的介绍 2. vector的使用 2.1 vector的定义 2.2 vector iterator 的使用 2.3 vector 空间增长问题 ...

  8. <初识C++(1)>《C++初阶》

    目录 <初识C++><C++>之学习 --By 作者:新晓·故知 1. C++关键字(C++98) 2. 命名空间 2.1 命名空间定义 2.2 命名空间使用 3. C++输入 ...

  9. C/C++内存管理模板初阶

    内存管理和模板初阶 1 内存管理 1.1 C/C++ 的内存分布 1.2 C 中动态内存管理方式 1.3 C++ 中动态内存管理方式 1.3.1 new/delete操作内置类型 1.3.2 new/ ...

最新文章

  1. 研究人员研发可自我修复的“电子皮肤”,重点是还能回收再利用
  2. ElasticSearch6 查询模板的创建使用
  3. [转]ASP.Net缓存总结
  4. maven工程的标准目录结构
  5. Java并发编程笔记之LinkedBlockingQueue源码探究
  6. linux chrome 安装过程记录
  7. mongoose 批量修改字段_WordPress图片路径批量替换方法
  8. Redis夺命十二问,你能扛到第几问?
  9. 计组之数据运算:12、加法器设计
  10. 菜鸟进阶Linux高手之路——第三天
  11. ucenter词语过滤原理
  12. AE插件 点线面三维粒子插件 Plexus Mac v3.1.8破解版
  13. 网页三剑客的一些序列号
  14. 怎么使用biopython_什么是Biopython? 你能用Biopython做什么? Biopython功能概。
  15. word打开老是配置进度_打开word文档显示配置进度怎么办 Word文档提示配置进度解决办法...
  16. 服务器系统补丁失败,windows2008系统更新补丁,以及失败解决方法
  17. Win7打开文件安全警告怎么关闭
  18. 奇志思达-微网站前端规则建议
  19. 在德国搜索超模海蒂和小龙女有风险哦
  20. 【论文精读】KD-MVS

热门文章

  1. HTML模仿QQ好友列表,JS+CSS实现类似QQ好友及黑名单效果的树型菜单
  2. 一篇文章全面了解运维监控知识体系
  3. leaflet-图层
  4. 解决微信播放背景音乐
  5. 新的开始--java
  6. 学历低,怎么找一份编程的工作?
  7. Linux 零拷贝技术
  8. List集合转String、String转List集合
  9. 数据增强之回译+EDA
  10. win10需要来自计算机管理员的权限才能,Win10明明是管理员却还需要权限怎么办?一招轻松解决它...