C++的引用类型

引用类型也称别名,它是个很有趣的东西。在c++ 下你可以把它看作是另外的一种指针,通过引用类型我们同样也可以间接的操作对象,引用类型主要是用在函数的形式参数上,通常我们使用它是把类对象传递给一个函数。 引用对象采用类型名加上&符号和名称的方式进行定义。例如:(int &test;),这里我们就定义了一个int类型的名为test 的引用,但是int &test;这样的方式是不能够被编译成功的,因为引用的定义必须同时给应用进行赋值操作,这里的赋值并不是说把变量的值传递给引用,而是把引用指向变量,写成这样就对了:(int &test=变量名;)。

#include <iostream>  
using namespace std;  

void main(void)  
{  

int a=10;  
int &test=a;  
test=test+2;  

cout << &a << "|" << &test << "|" << a << "|" <<test << endl;  

cin.get();  
} 

  观察并编译运行上面的代码你会发现&a和&test的地址显示是相同的,a和test的值显示也是一样的!

  结合前一个教程的内容我们来说一下const引用的相关内容,这里要特别注意,和前一个教程一样带const修饰的引用同样也容易混淆概念!

  const修饰如果用在引用上会有一个特别之处,它的奥妙就在于可以进行不同类型的对象的初始化,而这一切在普通变量操作上是不可能的下面我们来看一个例子:

#include <iostream>  
using namespace std;  

void main(void)  
{  

int a=10;  
//double &test = a + 1.2f; //这句就是错误的!  
const double &test = a + 1.2f;  

cout << &a << "|" << &test << "|" << a << "|" <<test << endl;  

cin.get();  
} 

  上面的代码足够说明问题了,这就是const修饰带来的好处,但是聪明的人会在输出的时候发现一个问题,就是a和test的值的输出不同,按照最先说的道理应该可以改变a的值呀,为什么在这里却有没有能够改变呢?

  道理是这样的,const修饰过后的引用在编译器内部是这样进行变化的。

int a=10;
const double &test = a + 1.2f;
  这样的一段代码在编译器认为却是下面的方式进行的

int a=10;
int temp = a;
const double &test = temp + 12.f
  这里其实是把a的值赋给了一个临时temp 变量,而后test获得的却是temp+12.f 改变的是temp而不是a,所以就出现了a和test显示的值不同的情况,这里要特别注意,这是一个很容易混淆的地方,在编写程序的时候要特别仔细,以免出现了问题却检查不出为什么!

#include <iostream>    
#include <string>    
using namespace std;  

void main(int argc,char* argv[])
{  
    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,char* argv[])      
{  
    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,char* argv[])      
{  
    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

#include <string>    
using namespace std;  

float c;  
float test(float,float);  
void main(int argc,char* argv[])      
{  
    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,char* argv[])      
{  
    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,char* argv[])      
{  
    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,char* argv[])      
{  
    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。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值