引用的超级起步

什么是引用

引用是某一变量(对象)的一个别名,对引用的操作与对变量直接操作完全一样。引用的声明方法:
类型标识符 &引用名=目标变量名

int a;
int &ra = a;//定义引用ra,它是变量a的引用,即别名

对引用的几点说明:
1:&在此不是求地址运算,而是起标识作用。
2:类型标识符是指目标变量的类型。
3:声明引用时,必须同时对其进行初始化。
4:引用声明完毕后,相当于目标变量名有两个名称,即该目标原名称和引用名,且不能再把该引用名作为其他变量名的别名。 ra=1; 等价于 a=1;
5:声明一个引用,不是新定义了一个变量,它只表示该引用名是目标变量名的一个别名,它本身不是一种数据类型,因此引用本身不占存储单元,系统也不给引用分配存储单元。故:
对引用求地址就是对目标变量求地址。&ra与&a相等
6:不能建立数组的引用。因为数组是一个由若干个元素所组成的集合所以无法建立一个数组的别名。

引用的用途:

引用的主要用途是为了描述函数的参数和返回值特别是为了运算符的重载。

1,引用作为参数
  引用的一个重要作用就是作为函数的参数。以前的C语言中函数参数传递是值传递,如果有大块数据作为参数传递的时候,采用的方案往往是指针,因为这样可以避免将整块数据全部压栈,可以提高程序的效率。但是现在C++中又增加了一种同样有效率的选择,在某些特殊情况下又是必须的选择,就是引用。
  

void swap(int &p1, int &p2) / /此处函数的形参p1, p2都是引用  
{ int p; p=p1; p1=p2; p2=p; }  
//为在程序中调用该函数,则相应的主调函数的调用点处,直接以变量作为实参进行调用即可,而不需要实参变量有任何的特殊要求

主函数:

int main( ) 
{ 
 int a,b; 
 cin>>a>>b; // 输入a,b两变量的值 
 swap(a,b); //直接以变量a和b作为实参调用swap函数  
 cout<<a<< ' ' <<b; //输出结果  
} 

上述程序运行时,如果输入数据10 20并回车后,则输出结果为20 10。

1,传递引用给函数与传递指针的效果是一样的。传递引用的时候,被调函数的形参就成为原来主调函数中的实参变量或对象的一个别名来使用,所以在被调函数中对形参变量的操作就是对其相应的目标对象在主调函数中的操作。
2,使用引用传递函数的参数,在内存中并没有产生实参的副本,它是直接对实参操作。而使用一般变量传递函数的参数,当发生函数调用时,需要给形参分配存储单元,形参变量是实参变量的副本,如果传递的是对象,还将调用拷贝构造函数。因此,当参数传递的数据
较大时,用引用比用一般变量传递参数的效率和所占空间都好。
3,使用指针作为函数的参数虽然也能达到与使用引用的效果,但是,在被调函数中同样要给形参分配存储单元,且需要重复使用”*指针变量名”的形式进行运算,这很容易产生错误且程序的阅读性较差,另一方面,在主调函数的调用点处,必须用变量的地址作为实参。
而引用更容易使用更清晰。

如果既要利用引用提高程序的效率,又要保护传递给函数的数据不在函数中被改变,就应使用常引用。

常引用

//不能通过常引用来修改其引用的内容
int n = 100;
const int &r = n;
r = 200;  //编译报错
n = 200;  //正常处理



常引用和非常引用的区别!!!
const T & 和 T &是不同的类型
T &类型的引用或者T类型的变量可以用来初始化const T &类型的引用。
const T类型的常变量和const T &类型的引用则不能用来初始化T &类型的引用。除非进行强制类型转化。

const int ival=1024;                                     
const int &refVal = ival;  // ok: both reference and object are const 
int &ref2 = ival;            // error: non const reference to a const object 
const达到了引用的安全性。
//假如有如下函数的声明:
string foo( ); 
void bar(string & s);

//那么下面的表达式将是非法的: 
bar(foo( )) 
bar("hello world");  

原因在于foo( )和"hello world"串都会产生一个临时对象,而在C++中,这些临时对象都是const类型的。因此上面的表达式就是试图将一个const类型的对象转换为非const类型,这是非法的。
指针和引用的差别 

 1. 非空的差别任何情况下都不能使用指向空值的引用.一个引用必须总是指向某个对象. 不存在的指向空值的引用这个事实意味着使用引用的代码效率比使用指针要高.  
2. 合法性区别在使用引用之前不需要测试他的合法性.指针必须测试.
3. 可修改区别 指针可以被重新赋值给另一个不同的对象.但是引用总是指向在初始化的时候被制定的对象,以后不能改变.但是指定的对象其内容可以改变. 应该使用指针的情况: 可能存在不指向任何对象的可能性 需要在不同的时刻指向不同的对象(此时,你能够改变指针的指向) 应该使用引用的情况: 如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,使用此时应使用引用。

总的来说,在以下情况下你应该使用指针,
一是你考虑到存在不指向任何对象的可能,在这种情况下,你能够设置指针为空;
二是你需要能够在不同的时刻指向不同的对象,在这种情况下,你能改变指针的指向。如果总是指向一个对象并且一旦指向一个对象后就不会改变指向,那么你应该使用引用。 


还有一种情况,就是当你重载某个操作符时,你应该使用引用。最普通的例子是操作符[]。
这个操作符典型的用法是返回一个目标对象,其能被赋值。 
  vector<int> v(10); //建立整形向量vector大小为10  
        //向量是一个在标准C库中的一个模板 [Page] 
  v[5] = 10; // 这个被赋值的目标对象就是操作符[]返回的值  
  如果操作符[]返回一个指针,那么后一个语句就得这样写: 
  *v[5] = 10;  
  但是这样会使得v看上去象是一个向量指针。因此你会选择让操作符返回一个引用,当你知道你必须指向一个对象并且不想改变其指向时,或者在重载操作符并为防止不必要的语义误解时你不应该使用指针。而在除此之外的其他情况下,则应使用指针 。

2,引用作为函数的返回值

#include <iostream>
using namespace std;

float temp;  

float fn1(float r)
{
        temp = r*r*3.14;
        return temp;
} 

int main()
{
        float a = fn1(5.0);
        cout<<a<<endl;
} 

/*
这种情况是一般的函数返回值方式。 返回全局变量temp值时,C++创建临时变量并将temp的值78.5复制给该临时变量。返回到主函数后,赋值语句a=fnl(5.0)把临时变量的值78.5复制给a。
*/
#include <iostream>
using namespace std;

float temp;  

float fn1(float r)
{
        temp = r*r*3.14;
        return temp;
} 

int main()
{
        float & a = fn1(5.0);//这里我们加入了引用
        cout<<a<<endl;
}

/*
这种情况下,函数fn1()是以值方式返回的,返回时, 复制temp的值给临时变量。返回到主函数后,引用b以该临时变量来初始化,使得b成为该临时变量的别名。由于临时变量的作用域短暂,所以b面临无效的危险。 根据C++标准,临时变量或对象的生命期在一个完整的语句表达式结束后便宣告结束,也即在“float& b=fnl(5.0);”之后,临时变量不再存在。 所以引用b以后的值是个无法确定的值。后来C++标准进行了扩展,规定如果临时变量或对象作为引用的初始化时,则其生命期与该引用一致。这样的程序, 依赖于编译器的具体实现,所以移植性是差的。

若要以返回值初始化一个引用,应该先创建一个变量,将函数返回值赋给这个变量,然后再以该变量来初始化引用,就像下面这样:
    int x=fnl(5.0);
    int& b=x;
*/
#include <iostream>
using namespace std;

float temp;  

float & fn2(float r)//这里又跟之前不一样了,,,
{
        temp = r*r*3.14;
        return temp;
} 

int main()
{
        float a = fn2(5.0);
        cout<<a<<endl;
}
/*
这种情况,函数fn2()的返回值不产生副本,所以, 直接将变量temp返回给主函数。主函数的赋值语句中的左值,直接从变量temp中得到复制,这样避免了临时变量的产生。当变量temp是一个用户自定义的类型时,这种方式直接带来了程序执行效率和空间利用的利益.(也就是避免了临时变量所带来的空间占有以及拷贝构造函数调用所带来的开销了)
*/
#include <iostream>
using namespace std;

float temp;  

float & fn2(float r)
{
        temp = r*r*3.14;
        return temp;
} 

int main()
{
        float & a = fn2(5.0);
        cout<<a<<endl;
}

/*
这种情况, 函数fn2()返回一个引用,因此不产生任何返回值的副本。在主函数中,一个引用声明d用该返回值来初始化,使得d成为temp的别名。由于temp是全局变量, 所以在d的有效期内temp始终保持有效。这样做法是安全的。
*/

/*
但是, 如果返回不在作用域范围内的变量或对象的引用, 那就有问题了。这与返回一个局部作用域指针的性质一样严重。BC作为编译错误,VC作为警告,来提请编程者注意。
*/
#include <iostream>
using namespace std;

//float temp;

float & fn2(float r)
{
        float temp = r*r*3.14; 
        return temp;
} 

int main()
{
        float & a = fn2(5.0);
        cout<<a<<endl;
}

这样会产生一个警告:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值