十.函数重载(重载、隐藏、覆盖)
- 函数重载
- 在同一作用域下,函数名相同,参数列表不同的函数,构成重载关系。
#include<iostream>
using namespace std;
int sum(int a,int b)
{
cout<<""<<endl;
retrun a+b;
}
float sum(float a,float b)
{
cout<< "" <<endl;
return a+b;
}
int main()
{
cout<< sum(1,9)<<endl;
cout<< sum(1.5f,1.9f)<<endl;
}
- 重载实现的机制
- C++代码在编译时会把函数的参数类型添加到参数名中,借助这个方式来实现函数重载,也就是C++的函数在编译期间经历换名的过程
- 因此,C++代码不能调用c函数(gcc编译出的函数)
注意:如果两个函数名一样,一定会冲突
- extern “C”
{
}
- 告诉C++编译器按照C语言的方式声明函数,这样C++就可以调用C编译器编译出的函数的函数了(C++目标文件可以与C目标文件合并生成可执行程序)
- 如果C想调用C++编译出的函数,需要将C++函数的定义用 extern “C” 来包括一下
-
重载和作用域
- 函数的重载关系发送在同一作用域下,不同作用域下的同名函数,构成隐藏关系。
-
重载解析
- 当调用函数时,编译器根据实参的类型和形参的匹配情况,选择一个确定的重载版本,这个过程叫重载解析。
- 编译器找到实参最佳的匹配函数,编译器将生成调用代码。
- 编译器找不到匹配的函数,会给出错误信息。
- 编译器找到多个匹配函数,但没有最佳函数,这种错误叫二义性。
- 大多数情况下编译器都能立即找到一个最佳的调用版本,但如果没有,编译就会进行类型提升,这样备选函数中就可能具有多个可能调用的版本,这样就可能产生二义性错误。
-
确定重载函数的三个步骤
- 候选函数
- 函数调用的第一步就是确定所有可调用的函数的集合(函数名,作用域),该集合就是候选函数。
- 选择可行函数
- 从候选函数中选择一个或多个函数,选择的标准(参数个数相同,而且通过类型提升实参可以被隐式转换为形参)
- 寻找最佳匹配
- 优先每个参数都完全匹配的方案,其次参数完全匹配的个数,在其次是浪费内存的字节数。
-
指针类型也会对函数的重载造成影响
- C++函数的形参如果是指针类型,编译时函数名中会追加 Px。(x根据类型变换)
十一.默认形参
- 在C++中函数的形参可以设置默认值调用函数,如果没有提供实参,则使用默认形参。
- 如果形参中只有一部分设置了默认形参,则必须靠右排列。
- 函数的默认形参是在编译阶段确定的,因此只能使用常量、常量表达式、全局变量
- 如果函数的声明和定义需要分开,那么默认形参必须设置在声明。
- 默认形参会对函数重载造成影响,设置默认形参时一定要慎重
十二.内联函数
- 普通函数调用时是生成调用指令(跳转)当代码执行到调用位置时跳转到函数所在的代码段中执行。
- 内联函数就把函数编译好的二进制指令直接复制到函数的调用位置。
- 内联函数的优点就是提高程序的运行速度(因此没有挑战,也不需要返回),但这样会导致可执行文件增大(冗余),也就是牺牲空间来换取时间。
- 内联分为显式内联和隐式内联
- 显式内联:在函数前 inline(C语言C99标准也支持)
- 隐式内联:结构、类中内部直接定义的成员函数,则该类型函数会被优化成内联函数。
- 宏函数在调用时会把函数体直接替换到调用位置,与内联函数一样也是使用空间来换取时间。
- 宏函数与内联的区别?
- 宏函数不是真正的函数,只是代码替换,不会有参数压栈、出栈以及返回值,也不会检查参数类型,所有类型都能使用,但这样会有安全隐患。
- 内联函数时真正的函数,被调用时会进行传参,会进行压栈,出栈,可以有返回值,严格检查参数类型,如果想被多种类型调用需要重载。
- 宏函数与内联的区别?
- 内联适用的条件
- 由于内联会造成可执行文件变大,并且增加内存开销,因此只有频繁调用的简单函数适合内联。
- 调用比较少的复杂函数,内联后并不显著提高性能,不足以抵消牺牲空间带来的损失,所以不适合内联。
- 带有递归特性和动态绑定特性的函数无法实施内联,因此编译器会忽略声明部分的inline关键字。
十三.引用
- 引用就是取艺名。
- 引用的基本特性
- 引用就是取别名,声明就是标识符为引用,就表示该标识符是另一个对象的外号。
- 引用必须被初始化,不存在空的引用,但有悬空引用(变量死了名还留着)。
- 可以引用无名对象和临时对象,但必须是使用常引用
- 引用不能更换目标
- 引用目标如果具有const属性,那么引用也必须带有const属性
- 引用一旦完成了定义和初始化就和普通变量名一样了,它就代表了目标,一经引用终生不能再引用其他目标。
#include<iostream>
using namespace std;
int main()
{
int a = 19;
int& b = a;
cout<< a << ' ' << b << endl;
}
- 引用型参数
- 引用当做函数的参数能达到指针同样的效果,到不具备指针的危险,还比指针方便。
- 引用可以非常简单实现函数间共享变量的目的,而且是否使用引用由被调用函数说了算。
- 引用当做函数的参数还能提高传递参数效率,指针至少还需要4字节内存,而引用只需要增加一条标识符与内存之间的绑定(映射)。
#include<iostream>
using namespace std;
void func(int& num)
{
num = 1000;
}
int main()
{
int num = 100;
func(num);
cout << num <<endl;
}
- 引用型返回值
- 不要返回局部变量的引用,会造成悬空引用
- 如果返回值是一个临时值(右值),如果非要使用引用接收的话,必须使用常引用。
注意C++中的引用是一种取别名的机制,而C语言中的指针是一种数据类型(代表内存编号的无符号整数)。
- 练习1:实现一个C++版本的swap函数。