引用

什么是引用

相当于数据的别名,对引用的一切操作相当于对原数据的操作

使用:
type &name = data;

引用在定义的时候就必须进行初始化,并且不能在再改变(只能是这数据的别名,不能变),类似于常量。

引用作为函数参数

当引用作为参数时,这是就相当于传递的是实参。和传指针类似。

当引用作为返回值时:不能返回局部数据(例如局部变量、局部对象、局部数组等)的引用,因为当函数调用完成后局部数据就会被销毁,有可能在下次使用时数据就不存在了。尤其是在栈内新建的变量,不能以引用的方式作为返回值。

引用的本质

引用其实是对指针进行了简单的封装,它的底层依然是通过指针实现的,引用占用的内存和指针占用的内存长度一样,在 32 位环境下是 4 个字节,在 64 位环境下是 8 个字节,之所以不能获取引用的地址,是因为编译器进行了内部转换。
例如:

int a = 99;
int &r = a;
r = 18;
cout<<&r<<endl;

实际上是

int a = 99;
int *r = &a;
*r = 18;
cout<<r<<endl;

只不过是编译器帮我们把解引用这个动作封装了起来。变量r同样在内存上开辟空间了的,存的就是a的地址,但是在使用r时,自动指向了a的地址。

和指针的区别
  • 定义时就必须初始化
  • const用法不同 (下面讲)
  • 不能多级引用 可以有二级三级指针,但引用没有
  • 指针可以+ - 但引用± 是对他代指的数据加减
临时数据

指针只能指向内存中的地址,下面的用法是错误的

int n = 100, m = 200;
int *p1 = &(m + n);    //m + n 的结果为 300
int *p2 = &(n + 100);  //n + 100 的结果为 200
bool *p4 = &(m < n);   //m < n 的结果为 false

表达式的结果并不会写入内存,而是放在寄存器中,用&来获取地址是不行的。
类似于表达式结果,函数返回值等。常量表达式都是不会分配内存的如100、200+34、34.5*23、

哪些数据会被写入寄存器?

寄存器离 CPU 近,并且速度比内存快,将临时数据放到寄存器是为了加快程序运行。但是寄存器的数量是非常有限的,容纳不下较大的数据,所以只能将较小的临时数据放在寄存器中。int、double、bool、char 等基本类型的数据往往不超过 8 个字节,用一两个寄存器就能存储,所以这些类型的临时数据通常会放到寄存器中;而对象、结构体变量是自定义类型的数据,大小不可预测,所以这些类型的临时数据通常会放到内存中。

下面这段代码在vs中可以通过且不会报错,可以说明这些类型的临时数据是保存在内存中的

#include <iostream>
using namespace std;

typedef struct{
	int a;
	int b;
} S;

S operator+(const S &A, const S &B){
	S C;
	C.a = A.a + B.a;
	C.b = A.b + B.b;
	return C;
}

S func(){
	S a;
	a.a = 100;
	a.b = 200;
	return a;
}
int main(){
	S s1 = { 23, 45 };
	S s2 = { 90, 75 };
	S *p1 = &(s1 + s2);
	S *p2 = &(func());
	cout << p1 << ", " << p2 << endl;
	return 0;
	可以正确的输出p1和p2的地址
}

但是在gcc编译器下是不通过的,因为p1,p2指向的是临时地址,在func函数结束后,其在栈上开辟的内容都会被释放,错误如下
this.cpp:32:19: error: taking address of temporary [-fpermissive]
S *p1 = &(s1 + s2);

this.cpp:33:18: error: taking address of temporary [-fpermissive]
S *p2 = &(func());

引用本质上和指针是一样的,引用也不能指代临时数据
bool isOdd(int &n){
    if(n%2 == 0){
        return false;
    }else{
        return true;
    }
}
int main(){
    int a = 100;
    isOdd(a);  //正确
    isOdd(a + 9);  //错误
    isOdd(27);  //错误
    isOdd(23 + 55);  //错误
    return 0;
}
引用和const

当定义一个引用并初始化后,这个引用就固定了,不能更改(相当于加了const)所以

int  a = 10;
int & const r = a;没必要,多此一举

那么 const int & r 呢?
可以通过a来修改变量,但是不能通过r来修改变量。这些和指针都是一样的

const还有什么做用
以上面代码为例

	S s1 = { 23, 45 };
	S s2 = { 90, 75 };
	S *p1 = &(s1 + s2);
	S *p2 = &(func());

这里我们说vs下可通过,但gcc下不行
将指针换成引用,同样vs可以gcc不行

  int main(){    
    S s1 = { 23, 45 };    
    S s2 = { 90, 75 };    
 	S &s3 = s1+s2;    
	S &s4 = func();                                                                                                      
    return 0;    
  }  

加上const,vs和gcc都可以通过,不过gcc会警告。

  int main(){    
    S s1 = { 23, 45 };    
    S s2 = { 90, 75 };    
W>  const S &s3 = s1+s2;    
W>  const S &s4 = func();                                                                                                      
    return 0;    
  }  

为什么:
常引用绑定到临时数据时,编译器采取了一种妥协机制:编译器会为临时数据创建一个新的、无名的临时变量,并将临时数据放入该临时变量中,然后再将引用绑定到该临时变量。注意,临时变量也是变量,所有的变量都会被分配内存。

为什么会为常引用创建临时变量:
临时变量是无法寻址的,因此不能写入。而常引用不要求写入,只需要读取数据即可。

下面的问题加上const都可以正确调用函数

bool isOdd(const int &n){  //改为常引用
    if(n/2 == 0){
        return false;
    }else{
        return true;
    }
}

int a = 100;
isOdd(a);  //正确
isOdd(a + 9);  //正确
isOdd(27);  //正确
isOdd(23 + 55);  //正确

给引用添加 const 限定后,不但可以将引用绑定到临时数据,还可以将引用绑定到类型相近的数据,这使得引用更加灵活和通用,它们背后的机制都是临时变量。
如:

float f = 12.45;
const int &r = f;
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值