下面我们要说一下,也是补充中最重要最需要掌握的内容,也是对传统函数操作的内存状态的一个补充学习。
下面我们来看一个例子:
#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 <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);//警告:返回的将是临时变量,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,这种方式是我们最为推荐的操作方式,因为不产生临时变量直接赋值的方式可以节省内存空间提高效率,程序的可读性也是比较好的。
它在内存中的情况见下图:
![](https://i-blog.csdnimg.cn/blog_migrate/fc1281220583ea72d77b536e09aa93c1.gif)
最后的一种情况是函数返回引用,并且发值赋给一个引用的情况:
#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。