一、函数重载(重载,隐藏,重写覆盖)
- 函数重载
在同一作用域下,函数名相同,参数列表不同,函数构成重载关 - 重载机制的实现
C++代码在编译时会把参数类型添加到函数名中,借助这个方式实现函数重载
也就是说C++函数在编译期间经历了更换名字的过程
因此,C++代码不能调用C函数(C语言编译器编译出的函数) - extern “C”{}
告诉C++编译器按照C语言的方式声明函数,这样 C++可以调用C编译器编译出的函数了
(C++目标文件可以和C目标文件合并生成可执行文件)
如果C想调用C++编译出的函数,需要将C++函数的定义用extern "C"包括一下
注意:如果两个函数名一样,一定会冲突 - 重载和作用域
函数的重载关系发生在同一作用域下,不同作用域下的同名函数构成隐藏 - 重载解析
当调用函数时,编译器根据实参的类型和行参的匹配度,选择一个确定的重载版本,这个过程叫做重载解析
实参的类型和行参的匹配状况有三种
- 编译器找到与实参最佳的匹配函数,编译器将生成调用代码
- 编译器着哦啊不到匹配函数,编译器将给出错误信息
- 编译器能够找到多个匹配函数,但没有一个最佳的,这种情况叫二义性
在大多数情况下,编译器都能立即找到一个最佳的调用版本,但如果没有,编译就会进行类型提升,
这样备选函数中及可能具有多个可能调用的版本,这样就可能产生而二义性错误
6、确定重载函数的三个步骤
- 候选函数
函数重载的第一步就是确定所有可调用的函数的集合(函数名、作用域),该集合中的函数就是候选函数 - 选择可行函数
从候选函数中选择一个或多个,选择的标准是参数个数相同,而且通过类型提升实参可被隐式转换为行参 - 寻找最佳匹配
这个优选每个参数都完全匹配的方案,其次是参数完全匹配的个数,再其次是浪费内存的字节数
7、指针类型也会对函数的重载造成影响
C++函数在编译时行参如果类型是指针类型,编译时函数名会追加Px。
二、默认行参
-
C++中的行参可以设置一个默认值,调用函数,如果没有提供实参,则使用默认行参
-
如果一个函数的行参只有一部分设置了默认行参,则必须将默认行参靠右排列
-
函数的默认行参是在编译阶段确定的,因此只能使用常量、或者常量表达式,或者全局变量
-
默认行参只需要在函数的声明设置中即可
-
设置默认行参时一定要慎重,默认行参会对函数重载造成影响(有冲突,或者二义性)
三、内联函数
-
普通函数在调用时是生成调用指令(跳转),然后当代码执行到调用位置时跳转到函数所在的代码段中执行
-
内联函数就是把函数编译好的二进制指令直接复制到函数的调用位置
-
内联函数的优点是提高程序的运行速度,因为没有跳转,也不需要返回,但是这样会牺牲代码空间,导致可执行文件增大,也就是牺牲空间换取时间
-
内联分为显式内联和隐式内联
现实内联:要在前面加关键字inline(C语言中C99标准也支持)
隐式内联:结构、类中的内部直接定义成员函数,则该函数也自动优化成内联函数
-
宏函数在调用时会把函数体直接展开替换到调用位置,与内联函数一样也是使用空间来换取时间
宏函数和内联函数的区别/优缺点
1)宏函数不是真正的函数只是代码的一个替换,不会有参数压栈、出栈以及返回值,也不会检查参数类型
因此所有类型都可以使用,但是这样会有安全隐患
2)内联函数是真正的函数,函数调用时会进行传参,压栈,出栈以及返回值,并且会严格检查参数类型,
这样就不能通用,如果想被多种类型调用,需要重载
-
内联函数适用的条件
由于内联会造成可执行文件变大,并且增加内存开销,因此只有频繁调用的简单函数适合内联,调用比较少的复杂函数,内联后并不显著提高性能,不足以抵消牺牲空间带来的损失,所以不适合内联
带有递归特性和动态绑定特性的函数无法实施内联,编译器会忽略声明部分的关键字
四、引用
-
引用的基本特性
1)引用就是取别名,声明一个标志符为引用,就表示该标志符是另一个对象的外号
2)引用必须初始化,不存在空引用,但有悬空引用(变量死了,名还留着)
3)引用不能更换目标
4)引用可以引用一个无名对象 int const&
5)可以引用临时对象但是必须是常引用
6)引用一旦完成了定义和初始化就和普通变量名一样了,它就代表了目标,一经引用,终生不能再引用其他目标
注意:引用目标如果具备了const属性,那么引用也必须附带const属性 -
引用型参数
1)引用当做函数的参数能达到指针同样的效果,但是不具备指针的危险,还比指针方便
2)引用可以非常简单实现函数间共享变量的目的,而且是否使用引用由被调函数说了算
3)引用当做函数的参数还能提高出传递参数的效率,指针至少需要4字节内存,而引用只需要增加一条标志符与内存之间的绑定(映射) -
引用型返回值
不要返回局部变量的引用,会造成悬空引用。
如果返回值是一个临时值(右值),如果非要使用引用接收,必须使用常引用接受
注意:C++的引用是一种取别名的机制,而C语言中的指针是一种数据类型(代表内存编号的无符号整数)
五、C++内存管理
-
new/delete C++中具备申请/释放堆内存功能的运算符
相当于C语言中的malloc和free
new+类型;会自动计算类型所需要的字节数,然后从堆中分配对应字节数的内存,并返回内存的首地址(具备类型)
delete 指针;会自动释放堆内存
注意:new 和 delete 与 malloc 和 free 不能混用,因为new和delete会自动调用类、结构的构造函数、析构函数 -
数组的分配和释放
new 类型 [n] ;n表示数组长度,会自动调用n次构造函数
delete[] 指针:通过new[]分配的内存必须通过delete释放
new []返回值前4个字节存放数组的长度 -
重复释放
delete / delete[]不能重复释放同一块内存
delete / delete[]释放野指针的后果不确定,但是可以释放空指针是安全的 -
内存分配失败
当分配的内存过大,没有能需求的整块内存就会抛出异常面试题 new/delete 和malloc相同点不同点
不同点
身份 运算符 标准库函数
参数 类型 自动计算 字节数 手动计算
返回值 带类型的地址 void*地址
调用构造 自动调用 不会自动调用构造/析构函数
出错 抛出异常 返回NULL
相同点
1、不能重复释放
2、可以释放NULL
3、都是管理的堆内存
注意:在C++中尽量使用 引用、new/delete