zoukankan      html  css  js  c++  java
  • 转: 引用作为函数返回值

    转自:http://blog.csdn.net/vivi_wang_11/article/details/7437382

    函数返回值时,要生成一个值的副本。而用引用返回值时,不生成值的副本。
      例如,下面的程序是有关引用返回的4种形式:
        //*********************
        //**   ch9_6.cpp  **
        //*********************

        #include <iostream.h>

        float temp;

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

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

        void main()
        {
         float a=fn1(5.0); //1
         float& b=fn1(5.0); //2:warning
         float c=fn2(5.0); //3
         float& d=fn2(5.0); //4
         cout<<a<<endl;
         cout<<b<<endl;
         cout<<c<<endl;
         cout<<d<<endl;
        }

      运行结果为:
        78.5
        78.5
        78.5
        78.5

      对主函数的4种引用返回的形式, 程序的运行结果是一样的。但是它们在内存中的活动情况是各不相同的。其中变量temp是全局数据,驻留在全局数据区data。函数main()、函数fnl()或函数fn2()驻留在栈区stack。
      第一种情况:见图9-5。


    图9-5 返回值方式的内存布局

      这种情况是一般的函数返回值方式。 返回全局变量temp值时,C++创建临时变量并将temp的值78.5复制给该临时变量。返回到主函数后,赋值语句a=fnl(5.0)把临时变量的值78.5复制给a。
    第二种情况:见图9-6。


    图9-6 返回值初始引用的情形

      这种情况下,函数fnl()是以值方式返回的,返回时, 复制temp的值给临时变量。返回到主函数后,引用b以该临时变量来初始化,使得b成为该临时变量的别名。由于临时变量的作用域短暂,所以b面临无效的危险。 根据C++标准,临时变量或对象的生命期在一个 完整的语句表达式结束后便宣告结束,也即在“float& b=fnl(5.0);”之后,临时变量不再存在。 所以引用b以后的值是个无法确定的值。BC对C++标准进行了扩展,规定如果临时变量或对象作为引用的初始化时,则其生命期与该引用一致。14.7节将进一步介绍这一内容。 这样的程序, 依赖于编译器的具体实现,所以移植性是差的。
      若要以返回值初始化一个引用,应该先创建一个变量,将函数返回值赋给这个变量,然 后再以该变量来初始化引用,就像下面这样:
        int x=fnl(5.0);
        int& b=x;
      第三种情况:见图9-7。


    图9—7 返回引用方式

      这种情况,函数fn2()的返回值不产生副本,所以, 直接将变量temp返回给主函数。主函数的赋值语句中的左值,直接从变量temp中得到复制,这样避免了临时变量的产生。当变量temp是一个用户自定义的类型时,这种方式直接带来了程序执行效率和空间利用的利益。
    第四种情况:见图9-8。


    图9—8 返回引用方式的值作为引用的初始化

      这种情况, 函数fn2()返回一个引用,因此不产生任何返回值的副本。在主函数中,一个引用声明d用该返回值来初始化,使得d成为temp的别名。由于temp是全局变量, 所以在d的有效期内temp始终保持有效。这样做法是安全的。
      但是, 如果返回不在作用域范围内的变量或对象的引用, 那就有问题了。这与返回一个局部作用域指针的性质一样严重。BC作为编译错误,VC作为警告,来提请编程者注意。例如,下面的代码返回一个引用,来给主函数的引用声明初始化:
        float& fn2(float r)
        {
         float temp;
         temp=r*r*3.14;
         return temp;
        }
        void main()
        {
         float &d=fn2(5.0); //error返回的引用是个局部变量
        }

      见图9-9说明。


    图9-9 返回的引用是局部变量

      如果返回的引用是作为一个左值进行运算,也是程序员最犯忌的。所以,如果程序中有下面的代码,则一定要剔除:
        float& fn2(float r)
        {
         float temp;
         temp=r*r*3.14;
         return temp;
        }
        void main()
        {
         fn2(5.0)=12.4; //error返回的是局部作用域内的变量
        }

     

    下面的这篇我应该学习作者的探究精神。

    将函数的返回值引用定义为引用

     “将函数的返回值定义为引用”是一种比较有效的做法。

    例如(分析见程序的注释): 

    1. #include<iostream>  
    2. #include<string>  
    3. using namespace std;  
    4. char & get_val(string &str,int i)//返回类型为引用  
    5. {//若去掉返回值的引用,则返回的是一个无名变量,其值是返回内容的一个拷贝而已,见下一个程序。  
    6.     return str[i];  
    7. }  
    8. int main()  
    9. {  
    10.     string s("123456");  
    11.     cout<<s<<endl;  
    12.     get_val(s,2)='a';   //即等价于:str[2]='a',因为get_val(s,2)返回的是引用,故相当于str[2]  
    13.     cout<<s<<endl;  
    14.     return 0;  
    15. }  

    对比一下没有返回引用的情况: 

    1. #include<iostream>  
    2. #include<string>  
    3. using namespace std;  
    4. char get_val(string &str,int i)//返回类型不为引用  
    5. {  
    6.     return str[i];  
    7. }  
    8. int main()  
    9. {  
    10.     string s("123456");  
    11.     cout<<s<<endl;  
    12.     char p;  
    13.     p=get_val(s,2); //因为函数get_val()返回值不是引用,所以必须赋值给一个变量后才能使用。  这句话有误:cout<<get_val(s,2)<<endl;是没有问题!
    14.     cout<<p<<endl;  
    15.     return 0;  
    16. }  

    显然这种函数返回值为引用的方法大有用武之地,最常见的就是用在重载流插入/提取运算符函数中,例如: 

    1. ostream & operate <<(ostream &output, Complex &c)//定义流提取运算符“<<”重载函数  
    2. {  
    3.        output<<”(”<<c.real<<”+”<<c.imag<<”i)”<<endl;  
    4.        return output;  
    5. }  
    6.   
    7. cout<<c3<<c2;    //调用重载后的"<<",等价于(operate <<(cout, c3))<<c2;   

    因为输出c3后紧接着要输出c2,所以要求cout<<c3(即operate <<(cout, c3))必须返回流提取对象cout,故必须使函数operate <<(cout, c3)的返回值能直接使用,即:必须将函数返回值设置为引用。

    请自行分析下面的程序: 

    1. int &func(int& a)  
    2. {  
    3.     return a;  
    4. }  
    5.   
    6. void main()   
    7. {  
    8.     int a=0;  
    9.     int b=func(a);  
    10.     printf("&a=%p, &(func(a))=%p, &b=%p ",&a, &(func(a)), &b);// %p也可以换做%08X,用于输出内存地址  
    11. }  

    执行的结果为:

    0012FF7C, 0012FF7C, 0012FF78

    a和b的地址不等。

    返回指针的情形

    例如: 

    1. #include <stdio.h>  
    2. char *RetMemory()  
    3. {  
    4.     char p[]="hello world ";  
    5.     return p;  
    6. }  
    7. void main( )  
    8. {  
    9.     char *str=NULL;  
    10.     str=RetMemory();  
    11.     printf(str);  
    12. }  

    分析:str能得到p吗?不能!因为函数调用结束后p的内存就被回收了,所以最好是利用传参数的形式定义p或者将p定义为全局变量。

    所以要避免返回 指向函数内部定义的变量或字符串常量 的指针。

  • 相关阅读:
    CSS
    人物
    CSS
    CSS
    概念- 工业4.0
    C#正则表达式
    六月定律
    c#中实现登陆窗口(无需隐藏)
    c#中关于String、string,Object、object,Int32、int
    一个快速找第k+1小的算法
  • 原文地址:https://www.cnblogs.com/kira2will/p/4004947.html
Copyright © 2011-2022 走看看