1 函数重载
1.1 什么是函数重载
在同一作用域下,函数名相同,只要参数列表不同(参数个数不同、类型不同)的函数就构成重载关系
1.2 函数重载与返回值类型、参数名无关
参数类型不同:普通类型不同、指针类型不同、带不带const属性不同
1.3 C++如何实现函数重载
g++ -S xxx.cpp生成xxx.s汇编代码可知,编译器会把函数的参数类型个数缩写追加到函数名末尾,也即是编译时函数名会根据参数名换名,所以换名后名字不同可以同时存在
因为在C++编译器中,编译函数时,会把函数编译成换名后的格式,并且调用函数也会找换名的函数,这样就无法继续调用之前已经同gcc编译器编译好的代码(不换名)
通过使用extern "C" 告诉C++编译器声明里面的函数按照C语言编译的方式编译函数(不换名),这样就可以让声明与定义匹配,可以继续正确调用标准C库、操作系统函数
1.4 重载与隐藏
只有在同一作用域下同名不同参的函数构成重载关系
在不同作用域(父子类)下同名函数遵循名字隐藏原则
1.5 参数类型转换
当调用函数时,编译器会优先调用类型匹配最精确的那个,如果没有最匹配的,会对参数进行部分类型提升,而不是直接报错
1.6 默认形参
1.6.1什么是默认形参
在C++中可以给函数的参数设置默认值,当函数被调用时,如果调用者提供了实参则使用实参,如果没提供则使用默认形参
返回值 函数名(类型 形参名 = 默认值)
1.6.2 默认形参的靠右原则
如果函数有多个参数,设置默认形参必须从右到左连续设置,否则有歧义
1.6.3 默认形参只能在声明中设置
如果函数声明与定义时分开实现的,只能在声明时设置默认形参,否则语法错误
1.6.4 默认形参可能会影响函数重载的效果
如果对函数进行重载,又设置了默认形参,调用时可能会冲突
因此重载过的函数设置默认形参要谨慎,避免二义性
void func(int num,int n=88);
void func(int nun); //有歧义
2 内联函数
2.1普通函数
会被翻译成二进制指令后存储在代码段中一份,调用语句会生成一条跳转指令,当进程运行到调用语句时,会跳转到函数所在的代码段位置执行,执行结束后再返回原位置
2.2声明是内联函数
内联函数也会被翻译成二进制指令后调用语句不再生成跳转指令,而是直接把函数的二进制指令替换调用语句,这样函数调用就没有发生跳转也没有返回,而是直接执行被调用函数,这种函数就称为内联函数
2.3实现内联函数
2.3.1 显示内联
在函数的返回值前面添加inline,该函数就会以内联机制调用
2.3.2 隐式内联
在结构、联合、类中声明以及定义的成员函数会自动被当做内联函数调用
如果只是在联合、结构、类中声明,但是在外部定义,则不会当做内联函数调用
注意:函数是否能够被内联执行取决于编译器以及它的优化等级(-On)决定,加inline只可能影响编译器的决定而已
注意:gcc编译器c99后也支持内联机制
2.4 内联适用的条件
2.4.1内联优点
节约了函数传参、跳转、返回的时间,提高代码运行效率
2.4.2 内联缺点
当内联函数在多个位置调用时,那么它的二进制指令就会拷贝多份,产生代码冗余,增大了可执行文件的大小
2.4.3 使用条件
适合:内容简单且频繁调用的函数,例如for(int i=0;i<10000;i++)}
不适合内容很多且极少调用的函数
具有递归特性的函数无法内联,编译器会自动处理为普通函数,会忽略inline关键字
2.4.4 内联函数(inline)与宏函数(define)的相同点与不同点
相同点:空间换时间策略,提高程序运行效率
不同点:
内联函数:返回值、类型检查、安全,入栈、出栈和调用
宏函数:运行结果、通用、危险
3 引用
3.1 什么是引用
一种取别名的机制
格式
int num = 10;
int& a = num;
为什么使用指针:
① 函数之间需要共享变量(输出型参数),引用可替代
② 提高函数之间的传参效率,引用可替代,甚至效率更高,因为引用不占用字节,没有拷贝
③ 与堆内存配合使用,必须使用指针
④ 配合字符串,但是string可替代
3.2 使用引用需要注意的问题
引用必须初始化,所以不存在空的引用
可以引用右值,但是必须使用const修饰
引用不可以中途更换引用的目标
返回值为引用类型时,不要返回局部变量的引用
3.3 什么情况可以使用引用
跨函数共享变量,使用引用比指针更安全(没有空引用,极少出现野引用),比指针更方便(无需取地址、解引用)
提高传参效率,引用比指针更高。因为指针还需要传递4/8字节,但是引用一个字节都不需要传递。但是引用也一样存在被修改的风险,因此为了保护引用目标的内容不被修改,也要加const保护
3.4指针与引用的相同点与不同点
相同点:都可以跨函数共享变量,都可以提高函数传参效率,都需要const保护内容
不同点:
引用是一种取别名的机制,指针是一种数据类型,可以定义指针变量
引用不需要额外的存储空间,指针变量需要4/8字节的内存来存储地址编号
引用必须初始化,指针可以不初始化
引用无空引用,指针可以有空指针
引用不可以与堆内存配合,指针可以
可以定义指针数组,但是不能定义引用数组
注意:可以存在数组的引用 int (&a)[10]=arr;
可以存在函数引用 void (&f)(void)=func;
4 强制类型转换
C++中依然可以使用C语言中的那套强制类型转换
为什么C++要重新设计一套强制类型转换的方式?因为C语言中的强制类型转换太危险
为什么C++强制类型转换设计得这么复杂、使用很麻烦?C++之父认为只因为程序设计不合理的情况下才需要强制类型转换,之所以设计复杂,就是不想让程序员轻松使用,从而反思自己的程序和重新设计自己的程序
4.1 静态类型转换
static_cast<目标类型>(待转换的数据)
目标类型与原类型之间必须有一个方向可以自动类型转换,否则报错
4.2 动态类型转换
dynamic_cast<目标类型>(待转换的数据)
目标类型与原类型之间必须存在继承关系且只能是指针或引用,否则报错
4.3 去常类型转换
const_cast<目标类型>(待转换的数据)
目标类型与原类型之间必须是指针或引用,且除了const属性不同之外,其他都需要相同才可以转换,否则报错
4.4 重解释类型转换
reinterpret_cast<目标类型>(待转换的数据)
只能把整数转换成指针或者指针转换成整数,否则报错