大家好,我是苏貝,本篇博客带大家了解C++的函数重载和引用,如果你觉得我写的还不错的话,可以给我一个赞👍吗,感谢❤️
目录
- 一. 预处理、编译、汇编、链接
- 二. 函数重载
- 1 概念
- 2 C++支持函数重载的原理—名字修饰
- 三. 引用
- 1 概念
- 2 引用特性
- 3 常引用
- 4 使用场景
- 5 引用和指针的区别
一. 预处理、编译、汇编、链接
在C/C++中,一个程序要运行起来,需要经历以下4个阶段:预处理、编译、汇编、链接
- 预处理:头文件展开,宏替换,条件编译,去掉注释
- 编译:检查语法,形成汇编代码。如果函数的声明和定义分离,那么main函数所在文件中有函数声明就可以编译成功
- 汇编:把汇编代码转成二进制机器码
- 链接:将同一项目文件经过汇编后形成的文件合并到一起,符号表的合并和符号表的重定位。
如果函数的声明和定义分离,那么此时编译器就会通过函数名到符号表(符号表可用于确定变量或函数在内存中的位置)里查找函数的地址(地址通过函数的定义确定),所以如果没有函数定义,编译器会报错
下面用Linux的文件经过上述4个步骤后形成的文件举例
因为预处理阶段会展开头文件,因此以.cpp为后缀的文件中就将头文件的内容拷贝了,所以预处理结束后,Stack.h文件不会再形成.i文件
二. 函数重载
1 概念
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
参数个数不同
参数的类型不同
参数的顺序不同
2 C++支持函数重载的原理—名字修饰
为什么C语言不支持函数重载,而C++支持呢?因为名字修饰
在C/C++中,一个程序要运行起来,需要经历预处理、编译、汇编和链接。链接时,面对调用的函数(如Add函数),链接器会使用哪个名字去找呢?这里每个编译器都有自己的 函数名修饰规则。由于Linux的函数名修饰规则简单,vs的较为复杂,因此我们下面用Linux来演示
用gcc编译器
写出test.c的代码
用gcc编译写出可执行程序,再使用objdump -S a.out来查看函数名修饰
我们发现,C语言的函数名修饰规则就是用自己的函数名,所以不能存在同名函数,否则编译器区分不开
采用g++编译器
将上面的test.c的内容拷贝到test1.cpp中,用g++编译形成可执行程序a.out,再执行命令objdump -S a.out查看函数名修饰
我们发现,C++的函数名修饰规则就不是用自己的函数名了。在Linux中,函数名修饰以_Z为前缀,后接函数名的字符个数,再接函数名,最后是函数形参的类型缩写(按顺序)。所以如果还有函数void f(int a),那么经过修饰,结果为_Z1fi
通过这里就理解了C语言没办法支持重载,因为同名函数没办法区分。而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。另外如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。
三. 引用
1 概念
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间
比如:李逵,在家称为"铁牛",江湖上人称"黑旋风"。铁牛和黑旋风就是李逵的别名
类型& 引用变量名(对象名) = 引用实体;
注意:引用类型必须和引用实体是同种类型的
第一个&表示引用,之后的两个&是对变量取地址。由结果知,引用变量b和它引用的变量a公用一块内存空间
根据上面的了解,看看下面代码的结果是什么?
答案是12,因为引用变量b和它引用的变量a公用一块内存空间
2 引用特性
- 引用在定义时必须初始化
- 引用定义后,不能改变指向
- 一个变量可以有多个引用
3 常引用
4 使用场景
- 做参数
我们对swap函数已经很熟悉了,之前函数的形参都是指针,现在我们用引用来当形参
注意:引用做形参时,引用变量名可以和实参名一样,但不建议
- 做返回值
查看下面代码,它的结果是什么?
结果是10,毫无疑问。那么,函数func1返回的是a吗?不是,a出了func1的函数作用域就被销毁,所以返回的是a的拷贝。
那下面代码的结果呢?
随机值。因为func1函数运行结束后,该函数对应的栈空间就被回收了,所以变量a的空间被回收,然而函数的返回值是变量a的别名,它是野引用,因此用ret接受函数返回值时也就是访问了已被回收的a的空间,故结果是随机值
如果我们仔细一点的话,我们还能看到编译器会报警告。至于为什么警告是说返回的是地址而非引用,我们下面会讲到
通过上面的了解,我们知道,如果函数的返回值出了该函数的作用域就被销毁(如局部变量),那就不能用引用返回。因此,能用引用返回的,是全局变量/静态变量/堆上变量等
5 引用和指针的区别
从语法上:
- 引用是别名,不开空间,指针要开空间
- 引用必须初始化,指针可以不初始化。所以引用更安全,因为没有空引用,但有空指针。容易出现野指针,但不容易出现野引用
- 引用不能改变指向,指针可以
- 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32 位平台下占4个字节,64位平台下占8个字节)
- 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小
- 有多级指针,但没有多级引用
- 访问实体方式不同,指针需要显式解引用,引用编译器自己处理
从底层上:
在汇编层面上,没有引用,都是指针,引用编译后也转成指针了。因此,引用在底层上也要开空间,但我们一般都说引用不开空间(语法层面)
好了,那么本篇博客就到此结束了,如果你觉得本篇博客对你有些帮助,可以给个大大的赞👍吗,感谢看到这里,我们下篇博客见❤️