❤️ 引用(&)
文章目录
💟 1、引用
🖊 1.1 概念
引用变量是一个别名,也就是说,它是某个已存在变量的名字。一旦把引用初始化为某个变量,就可以使用该引用名称或者变量名称来指向变量。
🖊 1.2 书写形式
类型& 引用变量名(对象名)=引用实体
int main()
{
int a = 10;
int& ra = a; // ra为a的引用
return 0;
}
✏️ 1.3 注意事项
1、引用不是新定义一个变量,而是给已存在变量取一个别名,编译器不会为引用变量开辟内存空间,它和它引用的变量共用同一块内存空间。
在vs编译器调试下我们可以看到,a和ra的值相同,并且他们的地址也相同。如图:
2、引用类型必须和引用实体类型是同种类型的。
int类型的数据必须使用相应类型的引用,否则编译器会报错!!!
🖤 2、引用特性
1、引用在定义时必须初始化
2、一个变量可以有多个引用
3、引用一旦引用一个实体,再不能引用其他实体
执行ra = b这条语句后,并不是改变了ra这个引用,ra仍为a的引用,从图中可以看到ra的地址仍为a的地址。
💛 3、常引用
int main() { const int a = 10; int& ra = a; //权限放大,不行 const int& rra = a; //权限平移可以 int b = 20; const int& rb = b; //权限缩小,可以 const int& rc = 10; //权限平移,必须加上const return 0; }
小结:
指针和引用赋值中,权限可以平移、缩小,但是不能放大。
所以当引用做参数时,为了防止权限放大,都是一般用const 引用
引用在可以被定义为const的情况下,应当尽量被定义成const。
💘 4、引用使用的场景
🐒 4.1 引用做参数
void Swap(int& left,int& right)
{
int tmp = left;
left = right;
right = tmp;
}
void Swap1(int* left,int* right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}
//引用做参数相当于C语言中的指针做参数
🐷 4.2 引用做返回值
//正确形式
int& Count_1()
{
static int n = 0; //静态变量,存储在静态区
n++;
return n;
}
//错误形式
int& Count_2()
{
int m = 0; //局部变量,存储在栈中
m++;
return m;
}
解释:
函数运行时,系统需要给该函数开辟独立的栈空间,用来保存该函数的形参,局部变量以及一些寄存器的信息等。
但函数运行结束后,该函数对应的栈空间就被系统回收了,但是对应的内存还在。
如果这时使用传引用返回,由于那块空间已经不属于此函数,再去访问数据时,得到的结果就是随机值。
解决方法:
- 为局部变量加上static修饰,这样变量就存储在静态区
- 使用全局变量(不推荐)
总结:
如果函数返回时,出了函数作用域,如果返回对象还在(还没返回系统),则可以使用引用返回,如果已经还给系统,则必须使用传值返回。
💚 5、引用和指针的区别
1、引用概念上定义一个变量的别名,指针存储一个变量地址。
ra的地址和a的地址相同,b的地址和 pb的地址不同。
2、引用在定义时必须初始化,指针则没有要求。
3、引用在初始化时引用一个实体后,就不能再引用其他实体,而指针可以在任何时候指向任何一个同类型实体。
可以将c的地址赋值给pb,即指针可以指向任何一个同类型的实体,但是引用不可以
4、没有NULL引用,但是有NULL指针。
5、在sizeof中含义不同:引用结果为引用类型的大小,但指针始终是地址空间所在字节个数(32位平台下为4个字节,64为平台下为8个字节)
6、引用自加即引用实体加1,指针自加即指针向后偏移一个类型字节的大小。
7、有多级指针,但是没有多级引用。
8、访问实体方式不同,指针需要显示解引用,引用编译器自己处理。
9、引用比指针使用起来相对更安全。