1.函数重载
函数重载:是函数的一种特殊情况,C++允许在同一作用域中声明几个功能类似的同名函数,这些同名函数 的形参列表(参数个数 或 类型 或 类型顺序)不同,常用来处理实现功能类似数据类型不同的问题。
C++支持函数重载,是因为C++编译器在编译阶段通过函数的参数类型、个数和顺序来决定调用哪个函数。
当我们定义多个函数名相同但参数类型、个数或顺序不同的函数时,C++编译器会根据实际调用时提供的参数信息来选择最匹配的函数进行调用。这个过程叫做重载决策。
具体的重载决策过程如下:
1. 编译器首先会查找与实际参数类型最为匹配的函数,如果找到一个完全匹配的函数,则直接调用该函数。
2. 如果没有找到完全匹配的函数,编译器会尝试进行隐式转换,将实际参数转换成形式参数类型,然后再次寻找最匹配的函数。
3. 如果有多个函数都能匹配,那么编译器会进行重载决策,选择最佳匹配的函数。这个过程会考虑函数参数的精确匹配、隐式转换的成本和二义性等因素。
需要注意的是,在进行函数重载时,函数的返回类型不会被考虑在内,只有参数类型、个数和顺序会影响函数重载的决策。
总之,C++的函数重载是通过编译器在编译阶段根据函数参数的类型、个数和顺序来决定调用哪个函数,以实现函数的重载功能。
简单来说就是C在找寻函数时使用其函数名去寻找的,如果函数名相同就找不到,而c++的中的名字和类型修饰规则可以区分每个函数,所以就可以调用不同函数。
#include <iostream>
// 函数重载示例
void print(int num) {
std::cout << "Printing an integer: " << num << std::endl;
}
void print(double num) {
std::cout << "Printing a double: " << num << std::endl;
}
void print(std::string str) {
std::cout << "Printing a string: " << str << std::endl;
}
int main() {
print(123); // 调用print(int)
print(3.14); // 调用print(double)
print("Hello, World!"); // 调用print(std::string)
return 0;
}
上述是函数重载的例子。
那么无参函数和有参函数会构成重载吗?答案当然是会的。
在那种情况下调用的时候还是会报错呢?
这里初次俺的错误其实是调用歧义,因为这两个函数都不给参数都可以调用。
如果我们将缺省参数去掉会怎么样呢?
这里看到就不会报错了,当我们将缺省参数去掉后,就需要传参了。
这种情况其实是Func函数重定义,就不是重载了,因为类型一样,个数一样,类型顺序一样。
2.引用
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空间,它和它 引用的变量共用同一块内存空间。
void TestRef()
{
int a = 10;
int& ra = a;//<====定义引用类型
printf("%p\n", &a);
printf("%p\n", &ra);
}
注意:引用类型必须和引用实体是同种类型的。
引用特点:
1. 引用在定义时必须初始化。
2. 一个变量可以有多个引用。
3. 引用一旦引用一个实体,再不能引用其他实体。
使用场景:
做参数:
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
做返回值 :
int& Count()
{
static int n = 0;
n++;
// ...
return n;
}
我们来看一串代码:
int& Add(int a, int b)
{
int c = a + b;
return c;
}
int main()
{
int& ret = Add(1, 2);
Add(3, 4);
cout << "Add(1, 2) is :"<< ret <<endl;
return 0;
}
结果会是什么呢?
这里就设计一个问题,为何打印出来的是随机值呢?
其实是因为我们创建的临时变量c是局部变量,当调用Add函数后,就会将其申请的栈帧返回给操作系统,此时的别名ret也就是随机了。我们可以将int c改为静态就可以了。
引用特点:
以值作为参数或者返回值类型,在传参和返回期间,函数不会直接传递实参或者将变量本身直接返回,而是 传递实参或者返回变量的一份临时的拷贝,因此用值作为参数或者返回值类型,效率是非常低下的,尤其是 当参数或者返回值类型非常大时,效率就更低。
引用和指针的区别:
在语法概念上引用就是一个别名,没有独立空间,和其引用实体共用同一块空间。
在底层实现上实际是有空间的,因为引用是按照指针方式来实现的。
引用和指针是C++中两种不同的概念,它们有以下区别:
1. 定义和初始化方式:指针变量使用 `*` 声明,需要通过取地址符 `&` 来获取变量的地址并初始化指针。而引用变量使用 `&` 声明,直接将变量名作为引用的别名。
2. 空值:指针可以为空,即指向空地址,表示不指向任何有效数据。引用必须在定义时初始化,并且不能为空。
3. 改变指向:指针可以在运行时改变指向,可以指向不同的对象或空地址。引用一旦初始化后就不能改变指向,始终引用同一个对象。
4. 使用方式:通过指针可以对对象进行操作,并且可以对指针进行解引用操作。引用作为对象的别名,和原始对象具有相同的行为。
5. 空间占用:指针需要占用额外的内存空间来存储地址值。而引用不需要额外的内存空间,因为它只是给原始对象起了一个别名。
6. 访问方式:通过指针可以改变指向对象的值,可以修改对象的内容。引用只是原始对象的别名,对引用的操作会直接作用于原始对象。
总的来说,指针提供了更灵活的操作和更多的控制能力,但也需要更加小心地处理空指针和指针的指向变化。而引用提供了更简洁的语法和更直观的代码,但对于指向不同对象的情况不适用。选择使用哪种方式取决于具体的需求和代码设计。
总结:
1. 引用概念上定义一个变量的别名,指针存储一个变量地址。
2. 引用在定义时必须初始化,指针没有要求。
3. 引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型 实体。
4. 没有NULL引用,但有NULL指针。
5. 在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下占 4个字节)。
6. 引用自加即引用的实体增加1,指针自加即指针向后偏移一个类型的大小。
7. 有多级指针,但是没有多级引用。
8. 访问实体方式不同,指针需要显式解引用,引用编译器自己处理。
9. 引用比指针使用起来相对更安全。
