C++入门必备知识
4.函数的重载
4.1 函数重载的概念
C语言中,我们不能定义两个同名的函数。
而在C++中允许定义同名函数,但这些函数的参数要有所区别,这样的同名函数就构成了函数重载。
这里的参数不同指的是:
1.参数类型不同;
2.参数个数不同;
3.参数顺序不同(本质上是类型不同);
4.2 C++支持函数重载的原因
简绍完函数重载;那在声明与定义分离的情形中,编译时,编译器是如何区分两个函数名相同的函数的呢?
C++给出了他的答案:C++并不像C语言那样直接用函数名去找函数的地址,它增加了函数名的修饰规则,函数名中引入了参数的类型。当然各种不同的编译器有自己的一套实现规则。下面我们用VS的规则进行说明:
5.引用(十分重要)
引用是C++中新增加的语法,他的出现与我们C语言阶段学习的指针共同协作能实现很多便捷的功能,因此它在C++中有着举足轻重的地位。
5.1 引用的概念
那引用究竟指什么呢?
我们不妨从生活场景切入:生活中,父母给你起了一个小名 a;学校中,同学给你起了一个小名叫做 b;此时父母叫a,同学叫b,其他人叫你的本名——这三种情况都是叫你本人。父母同学给你取的小名就是C++中说的引用。
引用:引用就是给已存在的变量取了一个别名
语法规则:int a=0;int& ra=a; //ra 是 a 的引用(ra 是 a 的别名)
由上图可看出,引用类型并不会另外开空间,它和它的引用的变量都共用一块内存空间,我们就可以具象的理解为取别名
5.2 引用的特性
了解了什么是引用,那如何正确的使用引用呢?
下面,我们会介绍引用的一些特性:
1.引用在定义时必须初始化(也就是起别名时就要明确对象是谁,引用与引用对象之间存在一个因果关系);
2.引用不能改变其引用对象(一旦确定别名,这个别名仅限于一个人用)
运行过后我们发现:a,b,c都变成2了!!!,说明b=c在这里是赋值,并不能改变b的引用对象!!!
3.一个变量可以有多个引用(一个人可有多个别名)
5.3 引用的应用场景
我们已经认识了引用,是否发现它与指针有着异曲同工之妙?那竟然已经有了指针威慑么还要有它呢?那当然是因为引用在一些场景的使用更加方便,下面我们就来具体看看引用的实际运用场景
5.3(1)做参数
我们写来例举一个十分简单的场景:
我们写交换两个数的函数时常用指针来传值:
void Swap(int* x,int* y)
{
int tep = *x;
*x =*y;
*y = tep;
}
那当我们学习了引用,以上代码可以改写为:
void Swap(int& x,int& y)
{
int tep = x;
x = y;
y = tep;
}
诶,我们发现运用引用之后我们不用传地址过去就能交换两个数了!!!!
有了这个简单的例子,大家是否能想起之前我们学习单链表时(头插)需要改变头结点的指针,我们总是要传二级指针:
void SLPushFront(SLNode** pphead,SLDatetype x)
{
assert(pphead);
SLNode* node = SLBuyNode(x);
node->next = *pphead;
*pphead = node;
}
学习了引用之后,我们直接传指针的引用(别名)即可,不用再传二级指针,极大的简化了代码理解难度与书写难度:
void SLPushFront(SLNode*& phead,SLDatetype x)
{
assert(phead);
SLNode* node = SLBuyNode(x);
node->next = phead;
phead = node;
}
在引用做参数的同时,若参数比较大还能够减少拷贝,提高效率:
到这我相信大家已经体会到C++中引用的好处,我们能妥善利用引用与指针这两大工具,能大大方便了我们日常编程。当然由于C++中引用不能修改引用对象这一特点,有些特定的情况我们只能利用指针(例如双向链表我们需要改变指向时就不能用引用了)。
5.3(2)做返回值
我们继续来看下一个场景——我们要写一个函数将函数内部的值返回:
我们返回的值并不是 a 本身,因为当函数调用结束时,a 的生命周期已经结束,我们可以再来回忆一下函数栈帧的创建与销毁:函数值是通过寄存器从函数中带回main函数中的。
而当我们使用了引用当返回值呢?
若返回a的引用代表返回的是a所在空间本身的值,不再用寄存机存储临时变量带回值,而此时a所在空间已经被销毁,内部可能为随机值不再是0!!!!!是错误的用法。
甚至返回接收值若都为引用,则更能验证上述操作的错误性,这就是经典的野引用:
结论:1.传值返回的是变量的一份拷贝,传引用返回返回的是变量本身的值
2.如果返回的变量出了函数生命周期结束,此时不能使用引用返回必须传值返回
那哪些只可以使用引用返回呢?
1.全局变量
2.静态变量
3.堆区空间等等…
引用做返回值的正确案例我们后续类和对象章节会反复遇到,此处不再具体举例。同时用引用做返回值和做参数一样也能提高效率,此处也不再详细赘述。
5.4 引用与指针
5.4(1)引用的语法与底层区别
语法层面:引用只是别名不开多余空间,和引用实体用同一块空间。
那实际上呢?这就不得不看看引用的底层了:
因此
底层方面:引用底层需要开辟空间的,因为引用是按照指针来实现的
5.4(2)引用与指针的区别
1.在语法层面,引用仅仅为别名不开空间;而指针是地址需要开空间
2.引用使用时必须初始化;而指针没特殊要求(可初始化也可不初始化)
3.引用不可改变其引用实体(对象);而指针则可在需要的时候改变其指向空间;
4.引用没有空引用,而且不易出现野引用;指针有空指针,容易出现野指针,相对指针,引用更安全
5.在sizeof中的区别:引用是引用的数据类型的大小;指针是地址空间所占字节数
6.引用自加是实体自加;二指针自加指向后偏移
7.无多级引用;但存在多级指针
5.5 常引用
在日常使用引用中,如果我们不希望引用去修改实体;此时就需要常引用来对引用进行修饰:
const int&
当对引用加上const修饰表示不能修改实体的引用,这实际上是一种权限的缩小;实际上引用的权限能缩小但绝对不能放大,如下就是对引用权利的放大:
我们知道以上这些性质,我们未来函数传引用参数时用const修时候便能传递表达式与常量:
同理: