--惯例:先上一段简单的代码
#include <iostream.h>
float temp; //定义一个全局变量
float fn1(float r)
{
temp =(float) (r*r*3.14);
return temp;
}
float& fn2(float r) //返回引用
{
temp = (float)(r*r*3.14);
return temp;
}
void main()
{
float a=fn1(5.0); //
// float& b=fn1(5.0); //VC++ 6.0 error, (其他的编译器可能是warning)
float c=fn2(5.0); //
float& d=fn2(5.0); //如果temp不是全局变量,仍然有可能报错;
}
分析上面的程序float a=fn1(5.0) 和 float c=fn2(5.0)编译时正常,这个很容易理解,就是一个简单的赋值语句,将函数的返回值赋给变量。而对于 float& b=fn1(5.0) 编译时不能通过,而float& =fn2(5.0)却能通过,却有点让人费解,这就要从运行时的结构和C语言的体系结构来看。
首先,C语言中函数的返回值类型是用来在运行的时候给函数的返回值分配空间,这个空间是分配在栈中。
如
int fun(void) //int 用于告诉编译器,在函数代码运行的时候给返回值分配一个int的空间,
{ //而这个空间是在栈中分配的:执行 a=fun() 等效以下几句话
int temp; // int temp; int s_temp=temp;a=s_temp;
return temp; //其中 int s_temp=temp 是由编译器在生成可执行代码对return temp生成的可执行代码
} //的等效C语句,即编译器在这个地方创建一个局部变量来给返回值分配一个空间,然后再将这个局部变量的值给要使用的这个返回值的变量。所以仅仅这种形式仅仅是简单的值传递。即使下面一种形式,返回的也仅仅是temp的值,而不是temp本身
int temp;
int fun(void)
{
return temp;
}
所以float& b=fn1(5.0); 这一句错误的原因是引用一个局部变量:float & b=s_temp;在引用b销毁前,s_temp已经销毁了,这样做是没有意义的,所以编译器要报错。
int temp;
int& fun(void)
{
return temp; //执行int &a=fun()等效为 int & s_temp=temp;int &a=s_temp;
}
float& d=fn2(5.0); 不会出错的原因是: a 是 s_temp的引用,s_temp是temp的引用,所以a 也是temp的引用,所以即使局部变量s_temp被删除了,但是全局变量temp依然存在,所以a 依然可以引用temp
但是当 temp是局部变量的时候上面就会出错。
通过一个类的调用来验证一下:
#include<iostream>
using namespace std;
class test
{
public:
test()
{
cout<<"默认构造函数"<<endl;
}
test(test & t)
{
cout<<"默认拷贝构造函数"<<endl;
}
~test()
{
cout<<"析构函数"<<endl;
}
test fun1(test & t) //此处是关键:test temp; 执行test & t=temp并不会调用构造函数或拷贝构造函数,;
{ //因为此处只是为temp建立一个应用,等效为常量指针,并不开辟内存。
test temp; //定义局部变量调用 默认构造函数
cout<<"fun1 called"<<endl;
return temp; //等效: test s_temp=temp;调用默认拷贝构造函数
}
};
int main()
{
test t1;//调用默认构造函数
test t2; //调用默认构造函数
t1.fun1(t2);//先调用默认构造函数,执行到返回值的时候再调用默认拷贝构造函数
return 0;
}
执行结果如下:(由于不会上图,只好手打,根据执行结果,可知上述关于返回值的假设是正确的。)
默认构造函数
默认构造函数
默认构造函数
fun1 called
默认拷贝构造函数
析构函数
析构函数
析构函数
析构函数