C++中函数返回引用,及问题

118 篇文章 7 订阅

目录

函数返回值 返回引用 (**)

C++基础知识 - 函数返回引用深度解析

关于函数调用返回引用错误并且每次调用不一致的分析与解决

将引用作为函数返回值的格式、好处和规则

实用经验 45 禁止函数返回局部变量的引用

---------------------------------------------------------

(1)返回的是一个引用类型,也就是返回值是绑定在该临时变量上

一旦该临时变量被销毁,就会导致返回的引用变量是一个不可预估的数值。
所以正确的做法是,将该函数的返回类型定义为一个const变量

const TSize TComPointCloud::getNumPoint() const
{
  return m_pos.size();
}

这样函数调用返回类型是一个变量,也就是将 该变量复制给 另外一个变量之 被销毁,因为是复制,两个变量之间是相互独立的。

(2)如果一个函数返回的类型为引用,那么,

返回的引用会绑定到该函数的返回 return value;

(变量value 就会绑定到 返回引用变量一旦该返回变量value被销毁,那么,返回的引用 就会绑定到 被销毁的变量,这个值是不确定的,这也就是每次调用时都会看到不同结果的原因)

===================================

实用经验 45 禁止函数返回局部变量的引用

请谨记

  • 函数返回时,保证返回数据超出函数范围后依然有效。像返回局部变量的引入就是不靠谱的事情。
  • 函数返回时,返回new生成的对象,同样不是一个可取的方法。因为这样的代码层次混乱,会让代码上层使用人员苦不堪言。

实用经验 45 禁止函数返回局部变量的引用_@一鸣惊人的博客-CSDN博客

将引用作为函数返回值的格式、好处和规则

格式:

类型标识符 &函数名(形参列表及类型说明){//函数体}

好处:

在内存中不产生返回值的副本(返回一个局部变量的引用是不可取的,因为随着局部变量生存周期的结束,相应的引用也会失效,产生runtime error)

注意:

  • 不能返回局部变量的引用,局部变量会在函数返回后被销毁。
  • 不能返回函数内部new分配的内存的引用,虽然不存在局部变量的被动销毁问题,但是函数返回的引用只是作为一个临时变量出现,并没有被赋予一个实际的变量,导致引用所指向的空间无法释放。
  • 可以返回类成员的引用,但最好是const,主要原因是当对象的属性与某种业务规则相关联的时候,其赋值常常与某些其它属性或者对象的状态相关联,因此有必要将赋值操作封装在一个业务规则中,如果其它对象可以获得该属性的非常量引用(或指针),那么对于该属性的单纯赋值就会破坏业务规则的完整性。
  • 流操作符重载返回值申明为“引用”的作用:<<和>>常常希望被连续使用,因此这两个操作符的返回值应该是一个仍然支持这两个操作符的流引用。可选方案包括:返回一个流对象和返回一个流对象指针。返回一个流对象,程序必须重新构造一个新的流对象,两个<<操作符实际上是针对了不同对象;返回一个流对象指针,并不能连续使用<<操作符。返回一个流对象引用则是唯一选择。

转载于:https://www.cnblogs.com/yingl/p/5815554.html

关于函数调用返回引用错误并且每次调用不一致的分析与解决

引用与指针的区别

1.引用不能为空,不存在空引用,但是指针却可以存在空指针,这就意味着我们拿到一个引用时不需要判断引用是否为空,但是如果拿到的是一个指针,则需要判断指针是否为空之后再进行指针的进一步操作。

2、引用一旦建立,将一直效忠于他,不能再引用。

引用与变量的区别

1.如果将一个变量绑定到一个引用上,那么如果这个引用发生改变,它所指向的变量也会发生改变。(这点非常重要,特别是在函数调用时,经常将函数的形参设计为引用类型,来达到形参发生改变实参改变的目的);

2.如果一个函数返回的类型为引用,那么,

返回的引用会绑定到该函数的返回 return value;

(变量value 就会绑定到返回引用变量,一旦该返回变量value被销毁,那么返回的引用就会绑定到被销毁的变量,这个值是不确定的,这也就是每次调用时都会看到不同结果的原因)

例如:函数getNumPoint的主要功能是返回变量m_pos的长度,但是该函数最终返回的类型是const 引用类型。

const TSize& TComPointCloud::getNumPoint() const
{
  return m_pos.size();
}

下面我们来分析一下这个函数错误的原因,该函数每次调用都会调用变量m_pos的一个内部函数size(),而该函数返回的变量是一个临时变量temp,该临时变量的有效域也就仅仅限制与函数getNumPoint作用域,也就是当函数getNumPoint调用return语句之后,该变量就会被销毁,但是我们现在

返回的是一个引用类型,也就是返回值是绑定在该临时变量上

一旦该临时变量被销毁,就会导致返回的引用变量是一个不可预估的数值。
所以正确的做法是,将该函数的返回类型定义为一个const变量

const TSize TComPointCloud::getNumPoint() const
{
  return m_pos.size();
}

这样函数调用返回类型是一个变量,也就是将改变量复制给另外一个变量之后被销毁,因为是复制,两个变量之间是相互独立的。

————————————————
版权声明:本文为CSDN博主「@眼里有星河」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_44312010/article/details/102767914

函数返回值 返回引用

 先看看该链接讨论的问题作为热身。http://topic.csdn.net/u/20070616/13/2a6a1739-1a69-4829-a90f-fdb19a61e095.html

函数返回值和返回引用是不同的

函数返回值时会产生一个临时变量作为函数返回值的副本,而返回引用时不会产生值的副本。

既然是引用,那引用谁呢?

这个问题必须清楚,否则将无法理解返回引用到底是个什么概念。以下是几种引用情况:

1,引用函数的参数,当然该参数也是一个引用

     const string &shorterString(const string &s1,const string &s2)
      {
             return s1.size()<s2.size()?s1:s2;
      }

      以上函数的返回值是引用类型。无论返回s1或是s2,调用函数和返回结果时,都没有复制这些string对象。简单的说,返回的引用是函数的参数s1或s2,同样s1和s2也是引用,而不是在函数体内产生的。函数体内局部对象是不能被因哟个的,因为函数调用完局部对象会被释放。

2,千万不要返回局部对象的引用

      const string &mainip(const string &s)
      {
             string ret=s;
             return ret;
      }

          当函数执行完毕,程序将释放分配给局部对象的存储空间。此时,对局部对象的引用就会指向不确定的内存。

3,在类的成员函数中,返回引用的类对象,当然不能是函数内定义的类对象(会释放掉),一般为this指向的对象,典型的例子是string类的赋值函数。

[cpp]  view plain  copy

    String& String::operator =(const String &str)  //注意与“+”比较,函数为什么要用引用呢?a=b=c,可以做为左值  
    {  
        if (this == &str)  
        {  
            return *this;    
        }  
        delete [] m_string;  
        int len = strlen(str.m_string);  
        m_string = new char[len+1];  
        strcpy(m_string,str.m_string);  
        return *this;  
    }  

这与sting类中的“+”运算符重载不一样。“+”运算符的重载不能返回引用,因为它返回的是在函数内定义的类对象,附上代码。

[cpp]  view plain  copy

    String String::operator +(const String &str)      
    {  
        String newstring;  
        if (!str.m_string)  
        {  
            newstring = *this;  
        }  
        else if (!m_string)  
        {  
            newstring = str;  
        }  
        else  
        {  
            int len = strlen(m_string)+strlen(str.m_string);  
            newstring.m_string = new char[len+1];  
            strcpy(newstring.m_string,m_string);  
            strcat(newstring.m_string,str.m_string);  
        }  
        return newstring;  
    }  

4,引用返回左值(上例的=赋值也是如此,即a=b=c是可以的)

      char &get_val(string &str,string::size_type ix)
      {
             return str[ix];
      }

      使用语句调用:
       string s("123456");
       cout<<s<<endl;
       get_val(s,0)='a';
       cout<<s<<endl;

最后转上一段code作为总结。

[cpp]  view plain  copy

    #include<iostream>  
    using namespace std;  
    string make_plural(size_t,const string&,const string&);  
    const string &shorterString(const string &,const string &);  
    const string &mainip(const string&);  
    char &get_val(string &,string::size_type);  
    int main(void)  
    {  
        cout<<make_plural(1,"dog","s")<<endl;  
        cout<<make_plural(2,"dog","s")<<endl;  
          
        string string1="1234";  
        string string2="abc";  
        cout<<shorterString(string1,string2)<<endl;  
          
        cout<<mainip("jiajia")<<endl;  
          
          
        string s("123456");  
        cout<<s<<endl;  
        get_val(s,0)='a';  
          
        cout<<s<<endl;  
          
        getchar();  
        return 0;  
    }  
    //返回非引用   
    string make_plural(size_t i,const string &word,const string &ending)  
    {  
        return (i==1)?word:word+ending;  
    }  
    //返回引用   
    const string &shorterString(const string &s1,const string &s2)  
    {  
        return s1.size()<s2.size()?s1:s2;  
    }  
    //禁止返回局部对象的引用(我的dev c++ 没有报错,比较可怕)   
    const string &mainip(const string &s)  
    {  
        string ret=s;  
        return ret;  
    }  
    //引用返回左值  
    char &get_val(string &str,string::size_type ix)  
    {  
        return str[ix];  
    }  


//函数返回值//

不带返回值的 return 语句只能用于返回类型为 void 的函数。

在返回类型为 void 的函数中,return 返回语句不是必需的,隐式的 return 发生在函数的最后一个语句完成时。

一般情况下,返回类型是 void 的函数使用 return 语句是为了引起函数的强制结束,这种 return 的用法类似于循环结构中的 break 语句的作用。例如,可如下重写 swap 程序,使之在输入的两个数值相同时不执行任何工作:

  // ok: swap acts on references to its arguments
  void swap(int &v1, int &v2)
  {
  // if values already the same, no need to swap, just return
  if (v1 == v2)
  return;
  // ok, have work to do
  int tmp = v2;
  v2 = v1;
  v1 = tmp;
  // no explicit return necessary
  }

这个函数首先检查两个值是否相等,如果相等则退出函数;如果不相等,则交换这两个值,隐式的 return 发生在最后一个赋值语句后。

返回类型为 void 的函数通常不能使用第二种形式的 return 语句,但是,它可以返回另一个返回类型同样是 void 的函数的调用结果:

  void do_swap(int &v1, int &v2)
  {
  int tmp = v2;
  v2 = v1;
  v1 = tmp;
  // ok: void function doesn't need an explicit return
  }
  void swap(int &v1, int &v2)
  {
  if (v1 == v2)
  return false; // error: void function cannot return a value
  return do_swap(v1, v2); // ok: returns call to a void function

  }

返回任何其他表达式的尝试都会导致编译时的错误。

返回类型不是 void 的函数必须返回一个值,但此规则有一个例外情况:允许主函数 main 没有返回值就可结束。如果程序控制执行到主函数 main 的最后一个语句都还没有返回,那么编译器会隐式地插入返回 0 的语句。

关于主函数 main 返回的另一个特别之处在于如何处理它的返回值。可将主函数 main 返回的值视为状态指示器。返回 0 表示程序运行成功,其他大部分返回值则表示失败。非 0 返回值的意义因机器不同而不同,为了使返回值独立于机器,cstdlib 头文件定义了两个预处理变量,分别用于表示程序运行成功和失败:

  #include <cstdlib>
  int main()
  {
  if (some_failure)
  return EXIT_FAILURE;
  else
  return EXIT_SUCCESS;
  }

我们的代码不再需要使用那些依赖于机器的精确返回值。相应地,这些值都在 cstdlib 库中定义,我们的代码不需要做任何修改。

函数的返回值用于初始化在调用函数处创建的临时对象。在求解表达式时,如果需要一个地方储存其运算结果,编译器会创建一个没有命名的对象,这就是临时对象。在英语中,C++ 程序员通常用 temporary 这个术语来代替 temporary object。

用函数返回值初始化临时对象与用实参初始化形参的方法是一样的。如果返回类型不是引用,在调用函数的地方会将函数返回值复制给临时对象。当函数返回非引用类型时,其返回值既可以是局部对象,也可以是求解表达式的结果。

例如,下面的程序提供了一个计数器、一个单词 word 和单词结束字符串 ending,当计数器的值大于 1 时,返回该单词的复数版本:

  // return plural version of word if ctr isn't 1
  string make_plural(size_t ctr, const string &word,
  const string &ending)
  {
  return (ctr == 1) ? word : word + ending;
  }

这个函数要么返回其形参 word 的副本,要么返回一个未命名的临时 string 对象,这个临时对象是由字符串 word 和 ending 的相加而产生的。这两种情况下,return 都在调用该函数的地方复制了返回的 string 对象。


当函数返回引用类型时,没有复制返回值。相反,返回的是对象本身。例如,考虑下面的函数,此函数返回两个 string 类型形参中较短的那个字符串的引用:

  // find longer of two strings
  const string &shorterString(const string &s1, const string &s2)
  {
  return s1.size() < s2.size() ? s1 : s2;
  }

形参和返回类型都是指向 const string 对象的引用,调用函数和返回结果时,都没有复制这些 string 对象。

注意:千万不能返回局部变量的引用,同理,也不能返回指向局部变量的指针。

当函数执行完毕时,将释放分配给局部对象的存储空间。此时,对局部对象的引用就会指向不确定的内存。考虑下面的程序:

  // Disaster: Function returns a reference to a local object
  const string &manip(const string& s)
  {
  string ret = s;
  // transform ret in some way
  return ret; // Wrong: Returning reference to a local object!
  }

这个函数会在运行时出错,因为它返回了局部对象的引用。当函数执行完毕,字符串 ret 占用的储存空间被释放,函数返回值指向了对于这个程序来说不再有效的内存空间。


返回引用的函数返回一个左值。因此,这样的函数可用于任何要求使用左值的地方:

  char &get_val(string &str, string::size_type ix)
  {
  return str[ix];
  }
  int main()
  {
  string s("a value");
  cout << s << endl; // prints a value
  get_val(s, 0) = 'A'; // changes s[0] to A

  cout << s << endl; // prints A value
  return 0;
  }

给函数返回值赋值可能让人惊讶,由于函数返回的是一个引用,因此这是正确的,该引用是被返回元素的同义词。

如果不希望引用返回值被修改,返回值应该声明为 const:

  const char &get_val(...


传值和传址的区别

传值时就是在内存中新开辟一个空间,将值赋给这个新开辟的空间,其生命周期为该函数调用结束时释放该空闲,计算结果不影响原调用数据内存空间的值。
传址时同样新开辟一个空间,但不同的是将所用数据空间的内存地址存在新开辟的空间中,即指针,函数在数据处理过程中由于是指针,即对源数据进行操作,操作后结果影响原数据。


打个比喻,一个盒子里有一个苹果,传值就是把这个苹果复制成另外一个苹果给要传的对象,至于这个对象怎么弄复制的苹果跟原来的那个没关系,而传值则是把盒子给了这个对象,那么这个对象要把这里面的苹果怎么弄那这个苹果就要改变,

  函数返回值 返回引用_函数返回值加引用_lx627776548的博客-CSDN博客

C++基础知识 - 函数返回引用深度解析

C++引用使用时的难点:

  •     当函数返回值为引用时, 若返回局部变量,不能成为其它引用的初始值,不能作为左值使用
  •     返回静态变量 或 全局变量的引用, 可成为其他引用的初始值, 也可以作为左值, 也可作为右值
  •     返回函数的普通形参作为引用, 不能作为其他引用的初始值, 不能作为左值使用
  •     返回函数的引用形参作为引用, 可成为其他引用的初始值, 也可以作为左值, 也可作为右值

#include <iostream>
#include <stdlib.h>
#include <stdio.h>

using namespace std;


int demo1() {
    int i = 0;
    //printf("i 的地址: %p, i=%d\n", &i, i);

    return i;
}

int &demo(int **addr) {
    int i = 666;
    *addr = &i;
    printf("i 的地址: %p, i=%d\n", &i, i);

    return i;
}

int &demo_static(int **addr) {
    static int i = 666;
    *addr = &i;
    printf("demo_static: i 的地址: %p, i=%d\n", &i, i);

    return i;
}

//3. 函数返回形参(普通参数)当引用
int &demo3(int var) {
    var = 666;
    return var;
}

//4. 函数返回形参(引用)当引用
int &demo4(int &var) {
    var = 666;
    return var;
}

int main(void) {
    int *addr = NULL;
    int ret = demo(&addr);

    

    //第一种情况  函数返回局部变量引用不能成为其它引用的初始值
    //int &i1 = demo(&addr);  
    //i1 = 888;
    //printf("addr: %p i1=%d\n", addr, i1);

    //demo(&addr);
    //demo1();
    //printf("addr: %p i1=%d\n", addr, i1);

    

    //第二种情况 函数返回局部变量不能做为左值
    /*demo(&addr) = 888;
    printf("1. addr: %p  value: %d\n", addr, *addr);
    demo1();
    printf("2. addr: %p  value: %d\n", addr, *addr);
    */

    

    //第三种情况  返回静态变量或全局变量可以成为左值或是其它引用的初始值
    //demo_static(&addr) = 888;
    int &i1 = demo_static(&addr);
    i1 = 888;
    printf("1. addr: %p  value: %d\n", addr, *addr);
    demo1();
    printf("2. addr: %p  value: %d\n", addr, *addr);
    demo_static(&addr);
      
    system("pause");
    return 0;
}

————————————————
版权声明:本文为CSDN博主「骆驼胡杨」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/qq_34606496/article/details/123363143

  • 3
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值