今天整理了一下关于C++中引用的用法
一、简单介绍
观察以下代码
#include <iostream> using namespace std;
int main()
{
int a=10;
int &b=a;
int &c=a;
int &d=b;
cout<<"a:"<<a<<endl<<"b:"<<b<<endl;
cout<<"c:"<<c<<endl<<"d:"<<d<<endl;
return 0;
}
执行结果:
这里 b,c, d 都是对a的引用
注意;引用并不是定义一个变量,而是给一个已知的变量起一个别名,我们可以通过修改
b,c,d,e的值来实现修改a 的值。
引用有以下特点:
1.一个变量可以有多个引用
2.引用必须初始化
3.当已经是一个变量的引用时,不能再成为其他变量的引用
当有如下代码时:
int &e=10;
编译器会报错:
我们想用e作为常量10的引用,假设成功了的话,那么我们可以通过e来修改我们的常量10吗?显然常量是不可以修改的。
当我们加上const 之后,
<span style="color:#ff0000">const int &a=10;</span>
a是一个常引用,即左右两边均具有常性,是可以通过的
但我们有以下代码时
int c=10;
float &d=a;
引用时类型不同也会直接报错
看看以下代码:
int a=10;
float b=a;
int c=10;
const float &e=c;
上面代码执行中其实是经历了如下过程:
这是因为当类型不一致时,我们有借助一个中间的临时变量来完成的
而临时变量时具有常性的,不能被改变的,d其实时临时变量的引用,所以当我们用const 修饰后就符合语法规则
二、引用作为参数
1.引用做参数想通过修改内部的值而完成改变外面的值
有如下代码:
#include <iostream> using namespace std;
void Swap(int &m,int &n)//引用做参数
{
int temp=m;
m=n;
n=temp;
}
int main()
{
int a=10;
intb=20;
cout<<"a: "<<a<<" b: "<<b<<endl;
Swap(a,b);
cout<<"a: "<<a<<" b: "<<b<<endl;
return 0;
}
执行结果;
上面的函数完成了两个数的交换,m,n分别是a,b的引用,交换了m,n的值而实现了交换a,b的值。
2.节省空间,提高执行效率
#include <iostream>
using namespace std;
typedef struct Data
{
int array[10000];
}Data;
void func(const Data &data)//引用做参数
{
//......
}
int main()
{
Data data;
func(data);
return 0;
}
这里函数的参数是一个结构体,如果我们采用值传递,形参也需要开辟一个结构体大小的空间,这样既占用空间还降低执行效率。但是如果采用引用就不会开辟空间。
但是这里需要注意,引用作为参数,可以通过修改函数内部的值而改变外面的值,有时,我们并不想让其改变外面的值。就需要传常引用。
三、引用做返回值
(1)值返回
#include<iostream>
using namespace std;
int Add(int a,int b)
{
int c=a+b;
return c;
}
int main()
{
int &ret=Add(1,2);
return 0;
}
以上代码程序会报错
其实当函数传值返回时,仍旧是创建了一个临时变量,而且这个变量一般存放在寄存器中,而主函数里的ret则是这个临时变量(具有常性)的引用,那么程序会报错,所以当给ret前面加const修饰时就符合语法规则。
以下为正确形式:
#include<iostream> using namespace std;
int Add(int a,int b)
{
int c=a+b;
return c;
}
int main()
{
const int &ret=Add(1,2);
return 0;
}
(二)引用返回
#include<iostream> using namespace std;
int& Add(int a,int b)
{
int c=a+b;
return c;
}
int main()
{
int &ret=Add(1,2);return 0;
}
这段代码虽然可以编译通过,但是会报一个警告
我们再看一种情况:
有点不好理解~
上面Add函数的返回值类型是引用类型,其实可以理解为中间借助一个引用类型(c的引用),而ret是那个引用类型的的引用,从而实现了ret是c的引用,而当函数结束,创建的函数栈帧也释放,c可能是一个随机值,(上述例子中第一次调用Add函数结束后ret仍旧是3,只是因为此处暂时还没有进行其他操做,c还没有被覆盖掉,而当第二次调用Add函数时,创建的是同一块栈帧,c还是之前的位置,ret仍旧是那块位置的引用,所以ret会被修改为30)但此处会有一个警告。
我们可以看到,与传值返回比较来看,用引用返回时是可以节省空间,但是也存在问题,如上面的例子,但是如果函数调用结束后,返回的这个变量还存在,就可以用引用做返回值。
所以这里有不要随便返回一个临时变量的引用。
可以通过函数代码的汇编看到不同:
上面可以看到,引用返回时将c的地址保存下来,最后再按照地址来找到c,这种方法与指针的传地址很相似。
而且注意到,这里的出了这个函数c被释放掉了,但是还要去取它的地址,感觉好像是越界访问了。
事实上,越界访问也不一定会报错,例如。
上面这种情况就会报错
而当我们访问上面这种情况就不会报错。
可以这样解释,一般情况下编译器会设置一些硬件保护,例如
其实我们从语法上说定义一引用类型是没有开辟空间的,但是从底层汇编来看,引用的实现和指针是一样的。
其实上面的用到引用的地方我们发现引用和指针有很多相似的地方。
例如实现两个数交换,传递的参数是一个很大的结构体时,等等。
总结一下引用和指针的联系和区别:
1.引用只能在定义时初始化一次,之后不能改变,而指针的值是可以改变的
2.引用必须指向有效的变量,而指针可以为空
3.sizeof指针时,是所指变量地址的大小,即指针本身的大小
sizeof引用时,是所引用变量的大小
4.对指针进行加1或减1操作时,即加或减所指对象的大小,即跨越一个所指对象的大小
对引用进行加1或减1操作时,即所引用的对象加1或减1.
5.相对来说,引用较指针安全一些,但是指针更加灵活一些。