《挑战30天C++入门极限》 对C++中引用的补充说明(实例)

 
 

对C++中引用的补充说明(实例)

#include <iostream>   
#include <string>   
using namespace std; 
 
void main(int argc,charargv[])

    int a=10; 
    int b=20; 
    int &rn=a; 
    cout<<rn<<"|"<<a<<endl; 
    cout<<&rn<<"|"<<&a<<endl;//c++中是无法取得应用的内存地址的,取引用的地址就是取目标的地址! 
    rn=b;//把引用指向另一个目标----变量b 
    cout<<&rn<<"|"<<&a<<"|"<<&b<<endl; 
    rn=100;//试图改变b的值 
    cout<<a<<"|"<<b<<endl;//输出修改后的结果  
    cin.get(); 
}


  由于引用本身就是目标的一个别名,引用本身的地址是一个没有意义的值,所以在c++中是无法取得引用的内存地址的。取引用的地址就是取目标的地址,c++本身就根本不提供获取引用内存地址的方法。

  引用一单初始化,就不在能够被指向其它的目标,虽然编译不会出错,但操作是不起作用的,实际上还是指向最先指向的目标。

  上面代码中的rn=b实际在计算机看来就是a=b,所以修改的还是a的值。

#include <iostream>   
#include <string>   
using namespace std; 
 
void main(int argc,charargv[])     

    int a=10; 
    void &rn=a;// 错误的,void即无类型的类型 
    int a[100]; 
    int &ra[100]=a;//错误,不能声明引用数组 
    cin.get(); 
}

  上面的两错误要记住引用的特性,void修饰是不能够声明引用的,引用是不能够声明数组的,即不能够声明引用数组。

  下面我们要说一下,也是补充中最重要最需要掌握的内容,也是对传统函数操作的内存状态的一个补充学习。

  下面我们来看一个例子:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float test(float,float); 
void main(int argc,charargv[])     

    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  在上面的代码中我们可能我们可能以为函数返回的就是c变量,呵呵。这么想可能就错了,普通情况下我们在函数内进行普通值返回的时候在内存栈空间内其实是自动产生了一个临时变量temp,它是返回值的一个副本一个copy,函数在return的时候其实是return的这个临时产生的副本。

  数据在内存中的情况如下图:

  上图明确表示了副本领事变量的情况。

  下面我们再来看一种情况,就是把返回值赋给引用:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float test(float,float); 
void main(int argc,charargv[])     

    float &pn=test(3.0f,1.2f);//警告:返回的将是临时变量,pn引用将成为临时变量的别名! 
    cout<<pn; 
    cin.get(); 

 
float test(float a,float b) 

    c=a*b; 
    return c; 
}

  float &pn=test(3.0f,1.2f);这句在bc中能够编译通过,因为bc扩展设置为临时变量设置引用,那么临时变量的生命周期将和引用的生命周期一致,但在vc中却不能通过编译,因为一但test()执行过后临时变量消失在栈空间内,这时候pn将成为一个没有明确目标的引用,严重的时候会导致内存出错。

  它在内存中的情况见下图:

  我们在图中看到,由于函数仍然是普通方法返回,所以仍然会有一个副本临时变量产生,只不过,这一次只是返回一个目标地址,在main中目标地址被赋予了引用pn。

  下面我们再看一种情况,这是返回引用给变量的情况:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,charargv[])     

    float pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种返回引用给变量的情况下,在内存中,test()所在的栈空间内并没有产生临时变量,而是直接将全局变量c的值给了变量pn,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,程序的可读性也是比较好的。

  它在内存中的情况见下图:

  最后的一种情况是函数返回引用,并且发值赋给一个引用的情况:

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,charargv[])     

    float &pn=test(3.0f,1.2f); 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  这种情况同样也不产生临时变量,可读和性能都很好,但有一点容易弄错,就是当c是非main的局部变量或者是在堆内存中临时开辟后来又被fee掉了以后的区域,这种情况和返回的指针是局部指针的后果一样严重,会导致引用指向了一个不明确的地址,这种情况在内存中情况见下图:

  由于这种情况存在作用域的问题,故我们推荐采用第三种方式处理。

  接下来我们说几个利用引用作为左值参与计算的例子,这一点一非常重要,对于理解返回引用的函数是非常有帮助的。

#include <iostream>   
#include <string>   
using namespace std; 
 
float c; 
float& test(float,float); 
void main(int argc,charargv[])     

    float &pn=test(3.0f,1.2f); 
    cout<<pn<<endl; 
    test(3.0f,1.2f)=12.1;//把函数作左值进行计算! 
    cout<<pn; 
    cin.get(); 

 
float &test(float a,float b) 

    c=a*b; 
    return c; 
}

  通常来说函数是不能作为左值,因为引用可以做为左值,所以返回引用的函数自然也就可以作为左值来计算了。

  在上面的代码中:

float &pn=test(3.0f,1.2f);

  进行到这里的时候pn已经指向到了目标c的地址了。

  接下来运行了

test(3.0f,1.2f)=12.1;

  把函数作左值进行计算,这里由于test是返回引用的函数,其实返回值返回的地址就是c的地址,自然c的值就被修改成了12.1。

 
 
posted @ 2019-07-15 10:00  landv  阅读(161)  评论(0编辑  收藏  举报