C++入门级——函数重载
目录
一. 概念
二. 名字修饰
三.extern "C"
1. C++程序调用静态库里的C语言程序
2. C语言程序调用静态库里的C++程序
一. 概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数或参数类型或参数类型的顺序)必须不同,常用来处理实现功能类似数据类型不同的问题
int Add(int left, int right)
{return left+right;
}
double Add(double left, double right)
{return left+right;
}
long Add(long left, long right)
{return left+right;
}
int main()
{Add(10, 20);Add(10.0, 20.0);Add(10L, 20L);return 0;
}
与返回类型无关,以下这个代码就是典型的不是函数重载!
short Add(short left, short right)
{return left+right;
}
int Add(short left, short right)
{return left+right;
}
二. 名字修饰
首先要知道:在C/C++中,一个程序要运行起来,需要经历以下几个阶段:预处理、编译、汇编、链接。
这里需要知道编译链接的具体过程每个过程做的处理,(建议复习一下再继续往下)这里的具体内容在预处理的那篇博客C语言程序环境与预处理
首先来看C语言程序中如果有函数重载会怎么样(为了更好理解在Linux系统下操作)
使用以下命名行,进行编译查看结果:
(gcc是C语言编译器,-o是编译指令,tc是编译通过后生成的文件名,test.c和f.c是被编译的两个文件)
gcc -o tc test.c f.c
这里发现,编译报错,不通过,说明C语言并不支持函数重载 !
解下来看看C++程序中的函数重载:
以上文件内容是一致的,但是除了头文件的后缀一致以外,其他两个文件后缀不一致,C语言文件后缀是.c,C++文件后缀是.cpp
使用以下命令行进行编译:
(g++是C++编译器,-o是编译指令,tcpp是编译通过以后生成的文件名,test.cpp和f.cpp是需要编译的C++文件)
g++ -o tcpp test.cpp f.cpp
以下是编译结果:
没有报错说明编译通过了!C++支持函数函数重载,并在改目录文件下生成了tcpp文件
通过以下指令,运行该文件,发现可以运行
./tcpp
通过以上程序知道了C++是支持函数重载而C语言不支持函数重载,那么,接下来是两个个很重要的问题!
为什么C++支持函数重载,而C语言不支持函数重载呢?C++又是如何支持的?
我们的项目通常是由多个头文件和多个源文件构成,当test.cpp中调用了f.cpp中定义的f函数时,编译后链接前,test.o的目标文件中没有f的函数地址,因为f是在f.cpp中定义的,所以f的地址在f.o中,但是test.o里有f的声明和调用,所以test.o里的f函数也有自己的地址,但是两者的地址并不一样(无论是编译还是汇编都是一个文件单独进行的)。链接阶段就是专门处理这种问题,链接器看到test.o调用f,但是test.o的符号表里没有f的地址(指的是定义里f函数的地址),就会到f.o的符号表中找f的地址,然后链接到一起。
编译时,C语言中没有对函数名进行处理,两个函数名相同,而符号汇总以后会生成符号表,在符号汇总时两个函数名一致,此时形成符号表是会出现冲突的,编译器报错。所以,由此可以猜想,在C++中支持函数重载的原因是对函数名进行了处理。
虽然没直接查看符号表但是可以通过以下指令在Linux系统上查看指令集
以下是查看C++的指令集:
objdump -S tcpp
使用该指令以后,找到两个函数的指令集:
会发现,两个函数的函数名果然不一致,在linux下,采用g++编译完成后,函数名字的修饰发生改变,编译器将函数参数类型信息添加到修改后的名字中。
以下是查看C语言的指令集:
objdump -S tc
指令集的结果如下:
会发现函数名和我们输入的函数名一致,没有改变,说明在linux下,采用gcc编译完成后,函数名字的修饰没有发生改变。
这就证明了,在C语言中,编译前并不会对函数名进行处理,导致了C语言并不支持函数重载,而C++对函数名进行了处理,支持函数重载
由此也能看出:g++编译的函数修饰后变成【_Z+函数名长度+函数名+类型首字母】
(每个编译器都有自己的函数名修饰规则)
如果想知道windows下对函数名的修饰,参考如下图:
可以看得出来非常复杂,想知道更多的C/C++函数调用约定和名字修饰规则:
C++的函数重载
C/C++ 函数调用约定
所以,我们就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区 分,只要参数不同,修饰出来的名字就不一样,就支持了重载。
值得一提的是:也从这里知道了为什么函数重载要求参数不同!而跟返回值没关系。
三.extern "C"
有时候在C++工程中可能需要将某些函数按照C的风格来编译,在函数前加extern "C",意思是告诉编译器,将该函数按照C语言规则来编译。
首先看这个程序:
其中的f是C++实现的,并且导入到当前这个C语言程序中,运行以后报错
int f(int t1, int t2);
int main()
{f(1,2);return 0;
}
链接时报错:error LNK2019: 无法解析的外部符号_Add,该符号在函数 _main 中被引用
我们知道,C语言程序调用C语言程序的库,C++程序调用C++的库是没问题的,那么C语言程序掉i用C++语言的库,C++程序调用C语言的库可以吗?可能会想到他们函数名的修饰都不一样,应该不能调用吧,事实上,是可以调用的。
首先就是C++调用C语言程序是比较简单的,有extern "C",在上述程序中加入这个以后,就不会报错了
extern "C" int f(int t1, int t2);
int main()
{f(1,2);return 0;
}
- 静态库的封装
为了更方便查看两者之间的调用关系,先将一个项目封装成静态库,配置方法如下:
最后应用和确定,这就将一个项目配置成静态库
- 导入静态库
以下是使用静态库的方法:
注意:后缀也需要
添加以后的结果如下:
最后点击应用和确定就配置好了
- C语言程序和C++程序的交叉调用
1. C++程序调用静态库里的C语言程序
以上操作做完后,此时运行程序,就会出现以下情况:
这是因为,虽然我们有了函数的声明,但是我们静态库里的程序是C语言程序,而我们引用的头文件是以C++的语法进行编译,我们现在使用的是C++的程序,由于声明是C++里的符号表,而定义却是C语言的符号表,根据上面函数的重载可知两者函数的名是不一样的,所以编译器去符号表里找不到,就导致链接错误
但是由于C++兼容C,所以只要在引用头文件的地方加上extern "C" 就行了,可以让头文件的编译的方式以C语言的方式编译,那声明的符号表里的函数名就和定义的符号表的函数就一致了,这样链接就通过了
(这里需要括号的原因是头文件编译后会展开头文件里的东西,一般会有很多行,我们是要头文件里的东西都以C语言的语法编译)
这是处理当C++的程序要调用C语言的程序的方法
2. C语言程序调用静态库里的C++程序
那么,如果我们要处理C语言调用C++程序怎么办?
这里先按上面的步骤创建再创建一个C++程序的静态库
然后让C语言程序调用C++的静态库,就会出现以下问题
因为我们虽然引用了头文件,但是是以C语言的语法进行编译,而库里的程序是C++的程序,和上面一样,声明和定义的符号表里的函数名不一致,C语言没有进行修饰而C++的函数进行了修饰,导致符号表合并时找不到函数的定义,就出现链接时错误
这个时候之前学的条件编译就派上用场了,可以在以下链接进行复习
C语言之程序环境和预处理
(之前C++程序调用C语言程序在C++程序上动手,这次C++程序调用C语言程序一样在C++程序动手)
对C++的头文件进行修改:
或者采用下面这种也行,但是更推荐上面那种,更方便
如上图,我们使用条件编译,让extern "C" 在C++的程序中得到编译,而C语言程序中不被编译,这样在定义的静态库文件里通过extern "C" 让C++程序以C语言语法编译,而在C语言程序里则不出现extern "C" ,并且C语言还是以C语言的语法编译
(extern "C"关键字是C++里特有的语法,C语言没有,extern "C" 能让C++程序以C语言的语法编译,如果该代码在C语言程序中出现则会报错;__cplusplus关键字是C++语法里特有的,在这里是告诉编译器在C++的程序中编译该条件里的代码,否则不执行该代码)
注意一个非常重要的问题,如果C语言程序想要调用C++的程序,C++的程序里不能出现函数重载,因为无论是C++程序的定义还是C语言的调用,都采用的C语言语法编译的,而C语言不支持函数重载
C++入门级——函数重载相关推荐
- C++ 笔记(13)— 函数(函数声明、函数定义、函数调用[传值、指针、引用]、函数参数默认值、函数重载)
每个 C++ 程序都至少有一个函数,即主函数 main() ,所有简单的程序都可以定义其他额外的函数. 1. 函数声明 函数声明告诉编译器函数的名称.返回类型和参数.函数声明包括以下几个部分: ret ...
- 函数重载需要注意的点
基本概念: 作用:函数名可以相同,提高复用性 函数重载满足的条件: 1.同一作用域 2.函数名相同 3.函数参数类型不同,个数不同,顺序不同 基本规则就上面那几条,不多说了,开始难一些的东西: 1.函 ...
- JavaScript中实现函数重载和参数默认值
2019独角兽企业重金招聘Python工程师标准>>> 参数默认值是指在调用函数时,若省略了某个实参,函数会自动为该参数分配一个默认值,使得函数调用的方便性和灵活性大大提高. 举个例 ...
- 《C++成员函数重载、覆盖与隐藏》
<成员函数的重载.覆盖与隐藏> ------------------------------------------------------------------------------ ...
- 函数重载(overload)
函数重载(overload) 先看一个例子 #include<iostream> using namespace std; //计算两个参数a,b的乘积的函数 int multiply(i ...
- 函数重载和 函数模板
一.函数重载(overload) #include <iostream> using namespace std;//计算两个参数a,b的乘积的函数 int product(int a,i ...
- C++类成员函数重载问题
C++类成员函数重载问题 #include <iostream> using namespace std; class A {private: int a;int b; public:A( ...
- c++学习笔记内联函数,函数重载,默认参数
c++学习笔记内联函数,函数重载,默认参数 1 inline内联函数 C++中的const常量可以替代宏常数定义,如: const int A = 3; #define A 3 C++中是否有解决 ...
- 在python中定义类时、运算符重载_自定义 Python 类中的运算符和函数重载(上)...
如果你对 Python 中的str对象使用过 + 或 * 运算符,你一定注意到了它的操作与 int 或 float 类型的区别: 你可能想知道同一内置运算符或函数如何对不同类对象进行不同操作的.这分别 ...
最新文章
- python pexpect
- C#做外挂常用API
- RBF(Radial Basis Function Network)+径向基网络
- cisco 访问控制列表ACL笔记
- php xxtea加密,PHP实现的XXTEA加密解密算法示例
- Google Chrome 调试JS利器
- java setter_java – 如何获得@getter和@setter?
- Android 动画 介绍与使用
- 【转】JMeter学习(二十七)Jmeter常见问题
- spark的java源码,Spark源码包的编译
- eligius矿池设置
- 点击图片实现图片放大
- 原生html+css+js制作宠物小精灵icon
- HBuilderX真机模拟uni-app项目 + 上架应用市场
- 三星android文件传输,最好的三星Galaxy S8管理器:如何将文件传输到三星Galaxy S8...
- pycharm 查看 python源代码
- [Kaggle Classify-Leaves] 树叶分类 score0.950
- php unix时间戳 秒,UNIX时间戳怎么在php项目中使用
- Python数据分析实战【第三章】2.5-Pandas数据结构Dataframe:基本概念及创建【python】
- oracle判断字符串以什么开头_oracle 如何查找特定字母开头的某个字段?