您现在的位置是:首页 >其他 >【C++】引用网站首页其他
【C++】引用
目录
?一.引用的概念?
引用不是定义一个新的变量,只是给一个变量其别名,其和其引用的变量公用一块内存空间
在我们日常生活中,经常会给身边的朋友和同学起外号,这里的起外号就相当于引用,比如鲁迅和周树人指的是同一个人
我们定义一个变量a和它的引用b,分别输出两者的值和地址
#include<iostream>
using namespace std;
int main()
{
int a = 10;//定义一个变量a
int& b = a;//b引用a
cout << a << endl;//输出a的值
cout << b << endl;//输出b的值
cout << &a << endl;//输出a的地址
cout << &b << endl;//输出b的地址
return 0;
}
输出结果
可以看到两者的值和地址都一样,因此验证了引用和引用的变量共用一块内存空间
示意图如下
即一块空间有两个名字
?二.引用的特点 ?
2.1引用必须初始化
我们在定义整形、指针等变量的时候可以定义的时候不初始化,不初始化的变量则是一个随机值,而在定义引用的时候必须初始化,即必须指明引用的是哪个变量,就像起外号一样,需要知道这个外号是给谁起的一样
以下代码编译错误
#include<iostream>
using namespace std;
int main()
{
int a = 10;//定义一个变量a
int& b ;//定义一个引用,但没有初识化
return 0;
}
报错信息显示必须初始化引用
2.2一个变量可以被多次引用
类似于取外号一样,一个人可以有很多个外号,所有的外号指的都是同一个人,一个变量同样可以有很多个引用,即一块内存空间有多个名称
我们对一个变量定义多个引用,然后输出它们的值和地址
#include<iostream>
using namespace std;
int main()
{
int a = 10;//定义一个变量a
int& b=a ;//b引用a
int& c = a;//c引用a
int& d = a;//d引用a
cout << a << endl;//输出它们的值
cout << b << endl;
cout << c << endl;
cout << a << endl;
cout << &a << endl;//输出它们的地址
cout << &b << endl;
cout << &c << endl;
cout << &d << endl;
return 0;
}
运行结果
可以看到,变量a和它的3个引用的值和地址全都是一样,验证了一个变量可以有多个引用
2.3引用一旦定义不可更改指向
我们给一个人起个外号,比如叫他小胖,引用的特性就相当于小胖这个外号就和他绑定了,只能指他一个人,不能用来叫别人
以下代码编译报错
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int c = 20;//定义一个变量a
int& b=a ;//b引用a
b = c;//b引用a改为b引用c,其实就是一个赋值操作,并没有改变引用的指向
&b = c;//编译报错
return 0;
}
由此验证引用一旦定义则不可改变指向
?三.常引用?
在c语言中有常变量,常指针等概念,它们都是利用const修饰而成,在C++中,用const修饰的引用称之为常引用
常引用即引用的变量不可以通过引用修改,即引用的变量变为只读
以下代码编译错误
#include<iostream>
using namespace std;
int main()
{
int a = 10;//定义一个变量a
const int& b = a;//定义一个常引用引用变量a
b = 20;//通过引用修改a的值
return 0;
}
由于定义了常引用引用变量a,故此时变量a对于引用来说作为只读变量,不可通过引用修改
在此引入权限放大、平移、缩小的概念
权限放大:即变量为只读,而引用却是可读可写,即引用的权限大于变量的权限,此时编译报错
以下代码编译错误
#include<iostream>
using namespace std;
int main()
{
const int a = 10;//定义一个常变量a
int& b = a;//定义一个引用引用变量a
return 0;
}
由于变量a为常变量,即变量本身变量为只读,而定义的引用却是可读可写,发生了权限放大,故编译报错
以上代码解决方法为把引用换为常引用
#include<iostream>
using namespace std;
int main()
{
const int a = 10;//定义一个常变量a
const int& b = a;//定义一个常引用引用变量a
return 0;
}
变量a的权限为只读,常引用的权限也为只读,编译则正常通过,该种情况成为权限平移
权限平移:即变量和引用的权限相同
以下代码正常编译
#include<iostream>
using namespace std;
int main()
{
const int a = 10;//定义一个常变量a
const int& b = a;//定义一个常引用引用变量a
int c = 20;//定义一个可读可写的变量
int& d = c;//定义一个可读可写的引用
return 0;
}
变量a和它的引用权限相同,都是只读,变量c和它的引用权限相同,都是可读可写
权限缩小:即引用的权限小于变量
以下代码编译正常
#include<iostream>
using namespace std;
int main()
{
int a = 10;//定义一个常变量a
const int& b = a;//定义一个常引用引用变量a
return 0;
变量a的权限为可读可写,而引用的权限为只读,引用的权限小于变量
总结:引用的权限可以为权限平移和权限缩小,不能为权限放大,即引用的权限必须小于等于变量
?四.引用的应用?
4.1做参数
以前我们实现交换两个数的函数,由于形参的改变不会影响实参,所以我们需要将形参设置为指针类型,而C++中可以将参数设置为引用,由于形参是实参的临时拷贝,需要时间和空间的开销,而引用只是实参的别名,可以直接操作实参,大大地提高了调用函数的效率
下面以交换函数为例
#include<iostream>
using namespace std;
void Swap(int& x, int& y)
{
int tmp = x;
x = y;
y = tmp;
}
int main()
{
int a = 10;
int b = 20;
cout << a << " " << b << endl;//交换前
Swap(a, b);
cout << a << " " << b << endl;//交换后
return 0;
可以看到我们利用引用作为函数参数,实现了两个数的交换
4.2做返回值
我们知道函数传返回值的过程是产生一个临时变量,然后将临时变量赋值给函数调用处,这样也会产生拷贝的过程,而传引用则解决了这个问题
首先来分析一下下面的代码
#include<iostream>
using namespace std;
int& Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int& ret = Add(a, b);
cout << ret << endl;
cout << ret << endl;
return 0;
}
可以看到第一次输出的结果是正确地,第二次输出的结果为一个随机值
这是为什么呢?
由于Add函数的z变量为局部变量,调用完函数之后空间就销毁了,注意空间销毁并不真的将空间销毁了,而是空间的使用权没有了,也就是空间依然在那,但是我们不能使用那块空间了,但是此时我们返回了它的引用就相当于野指针,通过引用访问就是非法访问,而期间这块空间可能被操作系统分配给了其他的函数,因此非法访问的值就是随机的
类似于我们订酒店一样,我们订了酒店之后就有了房间的使用权,退房时我偷偷地复制了一份钥匙,退房之后我就没有房间的使用权了, 但是我偷偷地用复制的钥匙再去开门就属于非法访问,此时我非法访问后可能这件房间在我退房之后没有人住,所以里面的样子和我退房时一样,也可能在我退房后清洁阿姨搞了卫生,也可退房之后被其他人使用了,所以非法访问的值是随机的,即结果是未定义的
我们在上述代码将函数的返回值改为Int,其他不变
#include<iostream>
using namespace std;
int Add(int x, int y)
{
int z = x + y;
return z;
}
int main()
{
int a = 10;
int b = 20;
int& ret = Add(a, b);
cout << ret << endl;
cout << ret << endl;
return 0;
}
此时编译报错
报错信息显示不是常引用的初始值必须为左值,即是可以修改的
原因分析:Add函数的z变量在函数调用完空间销毁,函数会设置一个临时变量进行拷贝然后赋值给函数调用处,而这个临时变量具有常量性,既可以视为常量,即为只读变量,而它的引用的权限是可读可写,属于权限放大,因此编译报错
总结:当函数调用后,引用的变量依然存在时,则可以返回引用,如静态变量、全局变量、其他函数栈帧的变量,其他情况只能使用传值返回
?五.引用和指针的区别?
引用在语法概念上是起别名,与引用的变量共用一块空间,但是在具体的物理层面,引用其实是开了空间的,其底层是利用指针的方式实现的
我们分别定义一个引用和指针,然后通过调试查看它们的汇编代码
#include<iostream>
using namespace std;
int main()
{
int a = 10;
int b = 20;
int& c = a;
int* d = &b;
return 0;
}
汇编代码
可以看到引用和指针的底层实现即汇编代码逻辑是一样的
引用和指针的区别
1.引用定义时必须初始化,指针定义时可以初始化也可以不初始化
2.引用在定义后不可以改变指向,而指针可以改变指向
3.没有空引用,但是有空指针
4.sizeof的作用不用,sizeof(引用)求的是引用的变量的大小,sizeof(指针)求的是指针变量的大小
5.引用++是引用的变量++,指针++则是向后偏移相应的位置
6.引用没有多级引用,指针有多级指针
7.引用比指针使用起来更安全
好啦,关于引用我们就学到这,如果对您有所帮助,欢迎一键三连,您的支持是我最大的创作动力!