这是老师上课讲的内容,现在把它写下来,一方面当做复习,另一方面真的想学点东西。废话不多说,先贴上测试的代码:
1 #include <iostream.h> 2 3 float temp; 4 5 float fn1(float r) 6 { 7 temp = (float)(r*r*3.14); 8 return temp; 9 } 10 11 float& fn2(float r) //绑定return的变量 12 { 13 //float temp; 14 15 temp = (float)(r*r*3.14); 16 return temp; 17 } 18 19 void main() 20 { 21 float a=fn1(5.0); // 22 float& b=fn1(5.0); //VC++ 6.0 error, (有的编译器warning) 23 float c=fn2(5.0); // 24 float& d=fn2(5.0); //如果temp不是全局变量,仍可能有问题 25 }
在分析上面的代码之前,先说两个理论:
(一)什么是引用。同时借助老师的简单比喻:
一个变量的引用就是这个变量的别名,引用在定义的时候必须同时初始化。老师是这样比喻的,说他有个同学(具体有无此人,无从考证)个子很矮,所以好多人给老段起了个外号叫做根号2,当然了根号2和老段是同一个人,对老段的任何操作都是对根号2的操作。后来又有人给根号2(也就是老段)起了另一个外号叫做矮冬瓜。这样,根号2、老段和矮冬瓜指的就是同一个人了。这里的老段就象征着变量的本体,根号2就是老段的引用,矮冬瓜就是老段的引用的引用。当然了,这三个称谓表示同一个人。
(二)一个函数的声明类型和它具体的返回值类型的关系:
首先知道一点:函数的返回值在内存的栈空间中是要开辟空间的。假设我们给这个空间起个名字叫做END,那么这个END的类型就是函数声明的时候的返回类型,不管你具体实际中返回的是什么类型。例如上面的fn2()函数:
1 float temp; 2 float& fn2(float r) //绑定return的变量 3 { 4 5 temp = (float)(r*r*3.14); 6 return temp; 7 }
函数返回的时候具体的语法是:float & END = temp;其中float就是这个函数声明时的类型,END就是给函数返回开辟的栈空间,temp就是实际return的东西。这就是用return的temp初始化END引用。这里temp就是老段,END就是根号2。
----------------------------------------------------------------------------------------------------------------------------------------
下面逐个的分析最开始的代码:
(1)
1 float a = fn1(5.0);
函数返回时的语法为:float END= temp;这样END就是一个float类型的变量。上面的这句代码就是用END这个变量初始化a。随后调用结束,栈空间的END就被os收回了。
(2)
1 float &b = fn1(5.0);
END为float类型的,函数返回时具体的语法是:float END= temp;这里的END也是一个float类型的变量。上面的这句代码在main函数中给END弄了一个引用(内部用指针实现的)。这里的b是个引用,也就是根号2,END就是老段。但是问题来了,END是暂时的栈空间啊,函数调用结束后它就被清除了啊,相当于在main函数的栈空间中弄了一个指针,但是这个指针指向了一个即将被清除的变量。当然会报错。
(3)
1 float c = fn2(5.0);
这里的END就是一个引用了,因为fn2声明的时候类型为float &。函数返回时的语法是:float & END = temp;用temp初始化一个引用。这里的END就是根号2,temp就是老段,也就是说对根号2的任何操作都会作用在老段的身上(他们是同一个东西)。所以这个语句的意思就是用temp给c初始化。
(4)
1 float &d = fn2(5.0);
同样END是引用,这里是用一个引用初始化另一个引用。这时temp是老段,END是根号2,d是矮冬瓜,三个名字说的是同一个人。即使调用结束后,END被清掉了,但是他毕竟是别名,不是变量的本尊,所以不会报错。
-----------------------------------------------------------------------------------------------------------------------------------------
注:作用在引用上的操作,同样会作用在变量本身上,但是如果这个引用的生存期到了,它要消亡了,这是没有关系的。就好像,忽然有一天大家都不在管老段叫根号2,那么根号2这个外号的生存期就到头了。但是这没什么影响。
但是老段不能消亡,你说老段消亡了,但是根号2这个外号还在,那不行,根号2指的是谁啊。
引用理论上是不占内存空间的,但是实际的实现上就是指针的操作。只不过这种指针的操作对编程人员是透明的。