重载
概念:在同一作用域内,函数参数列表不同 : 参数的个数 或 参数的顺序 或 参数的类型
代码演示:
补充
无参数和全缺省函数,不能定义为同一个函数名。如下图:
那么,为什么C语言没有函数重载呢?
这就得从编译链接的角度分析了。先来回顾一下编译和连接都干了什么。
预处理:头文件的包含/条件编译指令/宏展开等等…
−
>
t
e
s
t
.
i
-> test.i
−>test.i
编译:检查语法,是否匹配。
−
>
t
e
s
t
s
-> tests
−>tests
汇编:把汇编代码编译为机器指令。
−
>
t
e
s
t
.
o
-> test.o
−>test.o
链接:各自生成的 .o 文件链接在一起。
例如我们现在有这么一个工程:
我们都知道函数名其实就是个地址,但它的地址其实是第一条指令的地址,如下图,
而我们
t
e
s
t
.
c
p
p
test.cpp
test.cpp 中只有一个函数的声明,相当于只有那个
c
a
l
l
call
call 的地址,其实是没有函数的地址的。当我们把 函数的定义干掉的时候。
其实文件编译是还可以编译的过的。这个声明就相当于你知道了这里会有定义一样。所以才可以编过。
举个例子:
你现在想买一套房子,距离叫首付还差5万块钱,那么这时候你去找你的好哥们打了个电话,说:哥们,最近我打算买房子,但首付还差五万块钱,你能不能借我五万块钱?
你哥们说:没逝没逝,五万块钱没有问题。
那么这个时候,你心里有了个底,直接把定金就叫了。
那么此时我们运行一下?
可以看到此时我们虽然有了声明,但是缺少定义。这个问题就出在了链接上,我们找不到 Add() 对应的定义。这就相当于该交首付了,你哥们却说钱没了。
此时我们加上,成功运行。
回到问题上:那么,为什么C语言没有函数重载呢?
对于在一个工程里面(声明和定义不在同一个文件中)
那么链接的时候,我们就要那我们的函数名去对应的文件中 . o .o .o 文件的函数表去查找。
- C语言查找的时候直接拿函数名去查找的。
- C++拿修饰过的函数名去查找的。
先来看一下vs的修饰:
如上图:
在C++中,
?
f
@
@
Y
A
X
H
D
@
Z
?f@@YAXHD@Z
?f@@YAXHD@Z
这里的
H
H
H 代表
i
n
t
int
int,
D
D
D代表的是
c
h
a
r
char
char,这也是为什么C++支持函数重组,而C语言直接拿函数名去查找,比如上图的
A
d
d
Add
Add 。至于修饰规则是由编译器决定的。
引用
概念:就是给变量取个别名,而且共用同一片空间。
int x = 0;
int& p = x // 这里的 int& p = x,就相当于给 x 取个别名。
p = 1; // 此时由于都指向都一个空间,x 也变为 1.
这里要注意: 别名必须初始化,就是你不能直接这样:
int& p;
此时 p 和 x 的地址是一样的。
如上图:在C语言中,我们需要两个指针,而C++只需要对参数引用即可,此时我们函数参数(形参)里面的
x
和
y
x和y
x和y 就是 实参的
x
和
y
x和y
x和y
但是我们可以随便的取别名吗?
- 如上图此时我们的 x 是不能修改的,而我们的p是可以修改的,
从不能修改
−
>
可以修改
从不能修改->可以修改
从不能修改−>可以修改 权力放大了,这是不行的。
- 如上图,此时 x 从可以修改 − > 不可以修改 x从可以修改->不可以修改 x从可以修改−>不可以修改 权力缩小了,这是可以的。
小知识
- 当我们强制类型转换或者存在隐式类型转换的时候,他转换的结果会先存入一个临时变量中,而这个临时变量具有常属性,相当于被const修饰,所以我们那引用接受的时候就要注意权限放大缩小的问题。
此时虽然有个警告但是还是可以编译过的。
当我们使用引用的时候却不行。
相减或相加也都会产生隐式类型转换,所以我们使用引用的时候要注意。
引用能完全代替指针吗?
这里先来看一下指针与引用的不同:
- 指针指向一个地址,而引用则是给变量取别名
- 有空指针,无空引用(引用必须初始化)
- 当我们求指针类型的大小时,看得是环境(32位4字节),而引用则是对取别名的变量求类型大小
- 有多级指针,但没有多级引用
- 引用自加是对取别名的变量值进行自加,而指针的自加是偏移一个类型的大小
而且还记得在我们链表部分,当我们想要删除某个节点时,需要让这个节点的上一个节点的next指向,想要删除节点的下一个位置,这时候就需要改变指针的指向了,而我们的引用则不能改变指向,所以 引用不能改变指向 引用不能改变指向 引用不能改变指向 就是引用不能代替指针一个理由。
内联函数(inline)
概念: 以 inline 修饰的函数叫做内联函数,在调用的时候,直接展开,没有函数地址,避免了开辟栈帧的开销,提升了程序的运行效率。
这里我们先写一个加法函数。
可以看到此时我们是可看到,我们call了Add函数的地址去开辟了栈帧。
如上图,当我们加上Inline时,发现此时的Add并没有去 call,这说明并没有去开辟栈帧,而是直接计算了的,这就是内联函数。
同时在vs中,你并不能直接看到反汇编此时展开的结果,需要设置一下。
按上图流程设置一下,才可以看到。
那,函数我们都要使用内联函数吗?
显然不是,内联函数只适用于,代码量比较少的函数,举个例子:假设这里有一万个函数调用,而一个函数100行代码,那如果都用inline展开的话。
- 不展开:10000+100 (代码行数)
- 展开 :10000*100
所以内联函数只适用于代码量比较少的情况,而且内联函数是否展开,跟你没有关系,依据编译器决定的 O.o?。
内联函数不建议定义和声明分开写
内联函数展开函数,那么当你把声明和定义分开时,我们声明的地方展开后都没有函数地址,那么也无法去别的 .o 文件中的函数表查找。
auto关键字
简化长类型(以后再说)
auto 可以自动推导右边的类型。同时给了 typeid 可以查看类型。
auto使用注意事项:
- auto类型的变量必须初始化
- auto不能作为函数参数的类型
基于范围的for循环
以往我们遍历数组的时候只能如下遍历:
有了auto之后,
在有范围的集合中:
在有范围的集合中:
在有范围的集合中:还可以这样:
不过这只使用正向遍历,而且如果我们的 a 是个地址,那么也不可以。
空值
由于 C++在定义NULL的时候,直接定义为了0,所以在C11新引入了 n u l l p t r nullptr nullptr专门来表示指针空值,所以在C语言中我们用NULL来表示空指针,那么在C++中就只能用 n u l l p t r nullptr nullptr来表示了。