[C++]__引用和指针__

引用的概念及用法

  • 引用:给一个已经定义的变量重新起一个别名。引入了对象的一个同义词。定义引用的表示方法与定义指针相似,只是用&代替了*。对引用的操作与对变量直接操作完全一样。
  • 声明方法:类型标识符 &引用名=目标变量名;
  • 注意
    1. 一个变量可以有多个别名。
    2. 引用变量必须初始化。
    3. 只能在初始化引用一次,不能改变为再引用的其他变量。
     #include<iostream>
 int main() {
        int a = 10;
    int& b = a;//给a起一个别名叫做b  b引用了a b是a的别名
    int& c = a;//一个变量可以有多个别名
    int& d = c;//这种定义方式也是合理的
    //对其中任意一个别名进行赋值和修改,都会影响其他的值。
    system("pause");
    return 0;
}

  • const引用
    • const在C语言中:限定一个变量不允许被改变,产生静态作用。
    • const在C++中:它把一个对象转换成一个常量。
    • 如果所要引用的变量具有常性,则需要加上const。
    • 如果所要引用的是常数,也需要加入const。

const int a = 10;
const int& b = a;//如果要起别名的变量有const修饰,别名也要用const修饰

const int& a = 20;//如果要给常量起别名,也要用const修饰

int d = 10;
double e = d;//隐式类型转换
double& f = d;  //这是错误的,因为在隐式类型转换的时候,
                //会产生一个临时变量,而临时变量具有常性,
                //所以在引用的时候要用const修饰。
const double& g = d;//正确做法。   

这里写图片描述

  • 从这个图可以看出,在隐式类型转换的过程中会产生一个临时变量,而如果按照以上的代码引用d,f实际上是引用了临时变量,而临时变量具有常性,所以应该类似于引用常数,加上const做修饰。

    即:读写权限只能缩小,不能放大。  

引用做参数

  • 传值
void Swap (int left, int right){
    int temp = left;
    left = right;
    right = temp;
}

这段代码是不能实现交换的,在调用Swap(a,b)时,只是将a,b原来具有的值传给了left和right。例如,a = 1,b = 2,传值以后,left = 1,right = 2,此时在Swap函数内部交换left和right的值,只会使得left = 2,right = 1,而a,b还是原来的值,并没有改变。

  • 传址
void Swap (int* left, int* right){
    int temp = *left;
    *left = *right;
    *right = temp;
}

调用Swap(&a,&b)是可以完成交换的,因为传送的是a和b的地址,在Swap函数接收到地址之后,交换的时候就会解引用,找到a,b的位置,函数内交换的left和right,实际上就是在交换a和b的值,从而可以完成交换。

  • 传引用
 void Swap (int& left, int& right){
    int temp = left;
    left = right;
    right = temp;
}

调用Swap(a,b)即可完成交换。在这里,传的参数是引用,即形参是实参的别名。有点类似于传址,只不过传址是根据指针找到a,b,而传引用是根据别名找到a,b。即:left是a的别名,right是b的别名,交换left和right实际上就是交换了a和b。
举个例子,王小明的小名叫做明明,张小花的小名叫做花花,老师说:王小明和张小花换个位置,和,老师说:明明和花花换个位置的效果是一样的。


注意:
在这里也许有人会问,在前面不是说过,引用必须要初始化吗?可是这里并没有初始化呀!
其实在参数传递的过程中,是进行了初始化的 :int& left = a,int& right = b。


引用做返回值

  • 先看下面的一段代码
int Add(int a,int b){
    int c = a + b;
    return c;
}
int main(){
    int& ret = Add(1,2);//错误的,应该加上const
}

这段代码显然是错误的,在这里,返回的已经不是c了,因为c的生命周期已经到了。随着函数栈帧的销毁,c所占的空间已经返回给了内存,c已经释放了。此时返回的是一个临时变量(或称匿名变量,一般由寄存器充当)。既然是临时变量,就如我们之前所了解的,在ret引用的时候应该加上const做修饰,如果不加const也可以,可以让函数返回一个引用。

  • 这两种写法是正确的:
int Add(int a,int b){
    int c = a + b;
    return c;
}
int main(){
    const int& ret = Add(1,2);
}

int& Add(int a,int b){
    int c = a + b;
    return c;
}
int main(){
    int& ret = Add(1,2);
}

既然返回值是引用,那么就可以理解为此时返回了一个引用,假设返回了int& x(这个x就是为了方便理解起的名字,其实这个变量可能就没有名称,但是确实存在),它就是c的别名,即int& x = c,而在调用函数的时候,又给返回值起了一个别名叫做int& ret = x,所以,ret 是x的别名,x又是c的别名,这样ret也是c的别名,就可以获取到正确的返回值了。
这里写图片描述


  • 下面再看一组代码:
int& Add(int a,int b){
    int c = a + b;
    return c;
}
int main(){
    int& ret = Add(1,2);
    Add(10,20);
    cout << ret << endl;
    return 0;
}

在我的电脑环境运行输出的结果是30。为什么呢?还是要根据函数栈帧来解释,当第一次调用Add(1,2)结束后,栈帧被销毁,所占的空间已经还给了内存,但是根据不同的系统,可能那段空间内所存的值并没有被销毁,c刚刚使用过的空间的值还是3。第二次调用Add(10,20)时,虽然没有给它起别名,但是形成的栈帧在同一段区域,此时c还在原来的空间,那里的值已经变为了30。当我们输出ret的时候,会根据ret找到它的引用,也就是c所占那块空间的位置,里面的值是30,输出的结果就是30。但是这个结果是和操作系统有关系,看一段栈帧结束后系统会怎么样处理那段返回的空间,可能有的是30,有的就是随机值。
这里写图片描述


引用和指针的区别

  • 引用和指针的区别和联系

    1. 引用只能在定义时初始化一次,之后不能改变指向其它变量(从一而终);指针变量的值可变。
    2. 引用必须指向有效的变量,指针可以为空。
    3. sizeof指针对象和引用对象的意义不一样。sizeof引用得到的是所指向的变量的大小,而sizeof指针是对象地址的大小。
    4. 指针和引用自增(++)自减(–)意义不一样。
    5. 相对而言,引用比指针更安全。

      总结一下: 指针比引用更灵活,但是也更危险。使用指针时一定要注意检查指针是否为空。指针所指的地址释放以后最好置0,否则可能存在野指针问题。
      

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值