1. 函数重载
什么是函数重载?
C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
需注意是参数类型顺序不同,而不是参数名称不同:下面这样是构不成重载的
void Func(int a, char b);void Func(int b, char a);
函数重载不能依靠返回值的不同来构成重载,因为调用时无法根据参数列表确定调用哪个重载函数。
C++如何支持函数重载?
在C语言编译器编译后:
函数的名字的修饰不变,还是函数本身的名字。
在C++编译器编译后:
函数的名字的修饰发生改变,变成【Z+函数名称长度+函数名+函数参数类型首字母】
到这里就理解了C语言没法支持重载,因为同名函数没办法区分,而C++是通过函数修饰规则来区分,只要参数不同,修饰出来的名字就不一样,就支持了重载。如果两个函数函数名和参数是一样的,返回值不同是不构成重载的,因为调用时编译器没办法区分。
一句话概括:
C++通过名称修饰,在编译时对函数名进行编码,其中包括参数类型首字母,使同名但参数不同的函数能区分开,从而支持重载。
2. 引用和指针
引用和指针的区别?
特性 | 引用(Reference) | 指针(Pointer) |
---|---|---|
初始化 | 必须初始化,且不能改变绑定(类似指针常量 int* const ref) | 可以不初始化,可以修改指向(可重新赋值) |
空值(Null) | 不能为 null (必须绑定有效对象) | 可以为 nullptr (空指针) |
存储 | 引用不开空间 | 指针存储地址 |
安全性 | 更安全(不是绝对安全) | 可能野指针或空指针 |
用途 | 引用传参,传引用返回 | 动态内存分配 |
引用通常由编译器实现为自动解引用的常量指针。
引用的价值?(参数和返回值)
引用做参数可以提高效率,体现在大对象和深拷贝上,可以直接访问原对象:
- 如果函数参数是 普通变量(值传递),C++ 默认会进行 拷贝构造(调用拷贝构造函数),尤其是对于 大对象(如大的
std::vector
、std::string
、自定义类等),这会带来显著的性能损失。 - 某些对象(如包含动态分配内存的类)在传值的拷贝时会进行 深拷贝(Deep Copy),即复制所有底层数据,极其低效。
引用做返回值,出了函数作用域,如果返回对象还没销毁(成员变量、静态变量或全局对象的引用),则可以使用引用返回;如果已经返还给了系统(局部变量),则必须使用传值返回。
- 避免拷贝、提升效率
3. 建议用const、enum、inline去替代宏
宏的缺点?
宏函数的优势是不需要建立栈帧,可以提高效率,缺点是复杂,易出错,可读性差,不能调试。
inline函数有什么要求?
有宏函数的优点又没有宏函数的缺点。
要求:函数规模小、不能递归、频繁调用。否则编译器会自动忽略inline特性。
inline不建议声明和定义分离,分离会导致链接错误。
4. nullptr的意义
为什么要用nullptr代替NULL?
1. C++不允许void*到其他指针的隐式类型转换。NULL在C++中被定义成整数0,因为定义成void*无法隐式转换没有使用价值,定义成0可以保持与C代码的兼容性。
2. 但是对于C++特有的功能,比如函数重载,假设一个版本接收int,另一个版本接收int*,当你想要用空指针做参数去调用指针版本的重载时,用nullptr就可以,用NULL则会调用到整数版本,因为NULL是数字0,需要显式转化为int*。
#include <iostream>
using namespace std;void f(int)
{cout << "f(int)" << endl;
}void f(int*)
{cout << "f(int*)" << endl;
}int main()
{f(0); 第一个f(NULL); 第一个f((int*)NULL); 第二个f(nullptr); 第二个return 0;
}