目录
(最新更新时间——2025.1.9)
一个人的坚持到底有多难
在座的各位都能回答这个问题!
💕1.函数的重载
在C++中,是支持函数的名称重复的,但并不是完全重复
要求这些同名函数作用在同一作用域
且函数的形参不同->:可以是 参数类型不同 或者 参数个数不同 或者 参数顺序不同,这也叫做函数的重载
那么,函数的重载具体是什么呢?代码如下:
第一种情况:同名函数的形参类型不同——函数的重载
由代码可见,同名函数的形参是不同的,但是依旧可以运行,没有报错,我们也不必担心编译器会不知道运行哪个函数,因为在我们使用函数传参时,编译器会智能的去辨别类型,进而判断出该用哪个函数
第二种情况:同名函数的参数个数不同——函数的重载由此可见,同名函数的参数的个数不同可以构成函数的重载
第三种情况:同名函数的形参类型顺序不同——函数的重载
需要注意的是,形参的顺序不同需要满足同名函数的形参类型不能完全相同,如下:
如果这么写的话,编译器也会不知道该调用哪个函数,这叫做调用歧义
注意点:返回值不可以作为函数重载的判断,因为调用时也无法区分
小练习->:
判断下面代码是否构成函数的重载
namespace bit1 { void total(int pa, int pb) { cout << "total(int pa, int pb)" << endl; } } namespace bit2 { void total(int px, int py) { cout << "total(int px, int py)" << endl; } } int main() { bit1::total(3, 5); bit2::total(8, 8); }
答案是否定的,因为两个同名函数并不作用于同一作用域下,所以并不构成函数的重载
💕2.引用的定义
在C++中,提出了“引用”的功能,什么是引用呢?
引用其实就是变量的别名
就相当于,你们班有一个学习特别好的人,数理化每次都是第一,那么这个同学就会获得三个别名,分别是“数学第一”,“物理第一”,“化学第一”,当你说数学第一时,大家知道你说的是他,当你说物理第一时,大家也知道你说的是他,这就叫做别名
引用书写方法->:
我们知道,学习C语言时, & 是取地址的意思,难道在C++中改变了吗?
其实没有,只有在你像图中这么使用时才会变为引用的意义
此时,b为a的别名,那么我们调用b的时候,其实就是调用a
问题:我们在创建别名时,是开辟了新的空间吗?
我们可以验证一下:
我们发现,b与a的地址一样,所以在引用时,并不会开辟新的空间,只会创建一个“别名”
💕3.引用的一些常见问题
各位可以思考一下,这种情况下a,b会交换吗?
#include<iostream> using namespace std; void Swap(int& ta, int& tb) { int tmp = ta; ta = tb; tb = tmp; } int main() { int a = 100; int b = 20; Swap(a, b); cout << a <<" " << b << endl; }
答案是会的,为什么?
我们先回到C语言时学习的交换,在C语言中,我们知道,如果不传地址的话,就无法改变两者的值,因为形参是实参的一份临时拷贝,swap函数会将a与b复制一份传给ta,tb,这时候会开辟新的地址空间,操作的是 ta 与 tb
但是这样却交换了,首先我们知道,除去变量名就是变量类型,我们在形参中的变量类型是int&类型,这种类型本身就是引用,是属于一个别名,所以我们将a传过去时,会将a的引用
传给形参,实际上发生的是int& ta = a,int& tb = b;
此时ta与tb就是别名,而上述我们提到过,引用是不会开辟新的空间的,所以实际上操作的就是a与b
注意小点1->:
在使用引用的过程中,我们也可以给别名引用,起出别名的别名,如下->:
注意小点2->:
在使用引用时,必须进行初始化,不可以说创建int&后就扔掉了,等一会有需要再用,代码如下:
注意小点3->:
一个变量可以有多个引用,但一个引用只可以绑定一个变量
#include<iostream> using namespace std; int main() { int a = 10; int& b = a; int& c = a; int l = 100; c = l; }
以上代码的含义是什么?
A.让l赋值给C?
B.让c成为l的别名?
答案是A,引⽤⼀旦引⽤⼀个实体,再不能引⽤其他实体,所以这里是赋值
💕4.引用——权限的放大/缩小/平移
在C++中,存在权限的放大,缩小,以及平移的问题,那么什么是权限的放大/缩小/平移呢?
请看代码->:
个人认为,权限的放大可以分为地址类与非地址类,首先讲解非地址类
非地址类->:
//非地址时 //权限的平移 int a1 = 10; int& b1 = a1; //权限的缩小 int a2 = 100; const int& b2 = a2; //权限的放大 const int a3 = 100; //int& b3 = a3; 这里是错误写法 //正确书写 const int& b3 = a3;
权限的平移(合法)->:
我们的变量a1是 int 类型的,而引用的b1也是 int 类型的,这说明类型的功能没有改变,属于权限的平移
权限的缩小(合法)->:
我们的a2是 int 类型的,而引用的b2是const int 类型的,我们知道,被const修饰后的变量,只可以读不可以修改,所以我们把一个既可以读又可以修改的变量,取了一个只可以读不可以修改的别名,这是属于权限的缩小,是合法的
权限的放大(非法)->:
我们的a3是const int 类型的,只可以读不可以修改,但是如果我们运行了错误的写法
(int& b3 = a3),那么就把只能读不能改的变量,取了一个即可以读又可以改的别名,这样的话,我们的b3是可以修改的,但是b3是a3的别名,我的本体不可以被修改,我的分身却可以修改我,这是权限的放大,是非法的
地址类->:
//地址情况 int a1 = 10; //权限的平移 int* p1 = &a1; int*& pp = p1; //权限的缩小 int* pp1 = &a1; const int* p2 = pp1; //权限的放大 const int* p3 = &a1; int* p4 = p3; int*& p4 = p3;
原理与上述相同,但需要注意的是,权限的缩小并没有运用到别名,只是单纯的赋值,这也说明,权限的放大/缩小/平移,并不只是单独存在于引用之中,同时我们还需要注意一下写法
//权限的缩小 int* pp1 = &a1; const int* p2 = pp1; const int*& p3 = pp1; // 错误的写法
为什么?因为它单纯触发了C++中const的安全保障,为了防治你用错间接修改上,所以禁止使用
思考->:
//这是权限的放大吗? int a1 = 10; int b1 = 20; const int& r1 = a1 + b1;
答案是,是的,因为a1+b1得到的值是可以修改的,而r1不可以修改,所以这也是权限的放大
💕5. 不存在的空引用
在我们使用引用时,是不可以实现空引用的,如下->:
但是有一种写法是合法的->:如下:
int* ptr = NULL; int& r = *ptr; cout << r << endl;
只不过打印不出来什么
💕6.引用作为函数参数的速度之快(代码体现)
运行以下代码,可以知道引用作为参数的运行快在哪里
#define _CRT_SECURE_NO_WARNINGS #include<iostream> using namespace std; struct A { int a[10000]; }; void TestFunc1(A a) {} void TestFunc2(A& a) {} void TestRefAndValue() { A a; // 以值作为函数参数 size_t begin1 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc1(a); size_t end1 = clock(); // 以引用作为函数参数 size_t begin2 = clock(); for (size_t i = 0; i < 10000; ++i) TestFunc2(a); size_t end2 = clock(); // 分别计算两个函数运行结束后的时间 cout << "TestFunc1(A)-time:" << end1 - begin1 << endl; cout << "TestFunc2(A&)-time:" << end2 - begin2 << endl; } int main() { TestRefAndValue(); return 0; }
我们可以发现,不以引用作为参数的话,那么在运行函数前就会重新开辟新的空间来作为临时变量,这需要消耗大量的栈佂与时间
而如果用引用作为参数的话,那么就不会开辟新的地址,因为实际运行的是A& a = i;这属于引用创建的别名,没有开辟新的空间,正如上述所讲,地址没有发生改变
💕7.引用的思考
int main() { // 权限可以平移/缩小 不能放大 double d = 12.34; // 类型转换 int i = d; int& r2 = d;//报错 const int& r1 = d;//不报错 //r1不能修改d return 0; }
我们知道,在 int i = d 时,因为类型的不同,所以它其实是有一个隐式类型转换的,此时,会将 d 复制一份并强制转化为 int类型 再赋值给 i ,所以 i 其实是复制 d 的一份 int 类型的值
其次当我们使用int& r2 = d时,因为d是double类型的,r2是int类型的,这里属于运算,还是会存在隐式类型转换,所以 r2 其实是 强制转化后的 d ,但是因为强制转化所生成的值是临时变量,临时变量是不可修改的,但是int& r2是可以修改的,这属于权限的放大,所以是不对的
那为什么我们使用const int& r1 = d 时,就不会报错呢?这里其实还是会因为类型的不同而存在强制类型转化,只要是运算就会存在强制类型转化,此时d还是会被复制一份并强制转化为int类型,而 r1 就是强制 int 类型的 d 的别名,但是被const修饰了,那就说明我们不可以对其进行修改值,属于权限的平移,也就不会报错,如果你想打印 r1 的话,你会发现打印出来的是 12