目录
引用的概念和定义
引用的特性
引用的使用
const引用
指针和引用的关系
引用的概念和定义
引用不是新定义一个变量,而是给已存在变量取了一个别名,编译器不会为引用变量开辟内存空
间,它和它引用的变量共用同一块内存空间。
类型& 引用别名=引用对象;
C++中为了避免引入太多的运算符,会复用C语言的一些符号,比如前面的<<和>>,这里引用也和取地址使用了同一个符号&,大家注意使用方法角度区分就可以。(吐槽一下,这个问题其实挺坑的,个人觉得用更多符号反而更好,不容易混淆)
#include<iostream>
using namespace std;
int main()
{
int a = 0;
// 引⽤:b和c是a的别名
int& b = a;
int& c = a;
// 也可以给别名b取别名,d相当于还是a的别名
int& d = b;
++d;
// 这⾥取地址我们看到是⼀样的
cout << &a << endl;
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
我们可以看到,打印出来的四个地址完全相同
代表引用起到一个起别名的作用
引用的特性
引用在定义时必须初始化
一个变量可以有多个引用
引用一旦引用一个实体,再不能引用其他实体
#include<iostream>
using namespace std;
int main()
{int a = 10;// 编译报错:“ra”: 必须初始化引⽤//int& ra;int& b = a;int c = 20;// 这⾥并⾮让b引⽤c,因为C++引⽤不能改变指向,// 这⾥是⼀个赋值b = c;cout << &a << endl;cout << &b << endl;cout << &c << endl;return 0;
}
由图可知,a和b的地址相同,而c的地址不同,究其根本原因是无论是否初始化b
其和a指向的都是同一块空间,而后面写道的b=c则是赋值运算
其地址不会发生改变
引用的使用
引用在实践中主要是于引用传参和引用做返回值中减少拷贝提高效率和改变引用对象时同时改变被
引用对象。
int& STTop(ST& rs)
{
assert(rs.top > 0);
return rs.a[rs.top];
}
int main()
{
// 调⽤全局的
ST st1;
STInit(st1);
STPush(st1, 1);
STPush(st1, 2);
cout << STTop(st1) << endl;
STTop(st1) += 10;
cout << STTop(st1) << endl;
return 0;
}
使用引用接收返回值时,将寄存器中的值进行应用,从而使得函数调用出来的值进行运算
引用传参跟指针传参功能是类似的,引用传参相对更方便一些。
void Swap(int& rx, int& ry)
{
int tmp = rx;
rx = ry;
ry = tmp;
}
int main()
{
int x = 0, y = 1;
cout << x <<" " << y << endl;
Swap(x, y);
cout << x << " " << y << endl;
return 0;
}
于此,不用传递指针,通过引用传参,使得值同向变化,达到目的
引用返回值的场景相对比较复杂,我们在这里简单讲了一下场景,还有一些内容后续类和对象章节
中会继续深入讲解。
引用和指针在实践中相辅相成,功能有重叠性,但是各有特点,互相不可替代。C++的引用跟其他
语言的引用(如Java)是有很大的区别的,除了用法,最大的点,C++引用定义后不能改变指向。
Java的引用可以改变指向。
一些主要用C代码实现版本数据结构教材中,使用C++引用替代指针传参,目的是简化程序,避开
复杂的指针,但是很多同学没学过引用,导致一头雾水。
const引用
可以引用一个const对象,但是必须用const引用。const引用也可以引用普通对象,因为对象的
访问权限在引用过程中可以缩小,但是不能放大。
不需要注意的是类似
int& rb= a*3;
double d= 12.34;
int& rd = d;
这样一些场景下a*3的和结果保存在一个临时对象中
int&rd = d
也是类似,在类型转换中会产生临时对象存储中间值,也就是说,rb和rd引用的都是临时对象,而
C++规定临时对象具有常性,所以这里就触发了权限放大,必须要用常引用才可以。
所谓临时对象就是编译器需要一个空间暂存表达式的求值结果时临时创建的一个未命名的对象,
C++中把这个未命名对象叫做临时对象。
int main()
{
const int a = 10;
// 编译报错:error C2440: “初始化”: ⽆法从“const int”转换为“int &”
// 这⾥的引⽤是对a访问权限的放⼤
//int& ra = a;
// 这样才可以
const int& ra = a;
// 编译报错:error C3892: “ra”: 不能给常量赋值
//ra++;
// 这⾥的引⽤是对b访问权限的缩⼩
int b = 20;
const int& rb = b;
// 编译报错:error C3892: “rb”: 不能给常量赋值
//rb++;
return 0;
}
#include<iostream>
using namespace std;
int main()
{
int a = 10;
const int& ra = 30;
// 编译报错: “初始化”: ⽆法从“int”转换为“int &”
// int& rb = a * 3;
const int& rb = a*3;
double d = 12.34;
// 编译报错:“初始化”: ⽆法从“double”转换为“int &”
// int& rd = d;
const int& rd = d;
return 0;
}
指针和引用的关系
C++中指针和引用就像两个性格迴异的亲兄弟,指针是哥哥,引用是弟弟,在实践中他们相辅相
成,功能有重叠性,但是各有自己的特点,互相不可替代。
语法概念上引用是一个变量的取别名不开空间,指针是存储一个变量地址,要开空间。
引用在定义时必须初始化,指针建议初始化,但是语法上不是必须的。
引用在初始化时引用一个对象后,就不能再引用其他对象;而指针可以在不断地改变指向对象。。
引用可以直接访问指向对象,指针需要解引用才是访问指向对象。
sizeof中含义不同,引用结果为引用类型的大小,但指针始终是地址空间所占字节个数(32位平台下
占4个字节,64位下是8byte)
指针很容易出现空指针和野指针的问题,引用很少出现,引用使用起来相对更安全一些。
究其根本,他们的底层汇编指令完全相同