返回顶部
首页 > 资讯 > 后端开发 > 其他教程 >C/C++ 引用作为函数的返回值方式
  • 210
分享到

C/C++ 引用作为函数的返回值方式

2024-04-02 19:04:59 210人浏览 泡泡鱼
摘要

目录case1:用返回值方式调用函数case2:用函数的返回值初始化引用的方式调用函数case3:用返回引用的方式调用函数case4:用函数返回的引用作为新引用的初始化值的方式来调用

语法:类型 &函数名(形参列表){ 函数体 }

特别注意:

1.引用作为函数的返回值时,必须在定义函数时在函数名前将&

2.用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本

//代码来源:RUNOOB
#include<iOStream>
using namespace std;
float temp;
float fn1(float r){
    temp=r*r*3.14;
    return temp;
} 
float &fn2(float r){ //&说明返回的是temp的引用,换句话说就是返回temp本身
    temp=r*r*3.14;
    return temp;
}
int main(){
    float a=fn1(5.0); //case 1:返回值
    //float &b=fn1(5.0); //case 2:用函数的返回值作为引用的初始化值 [Error] invalid initialization of non-const reference of type 'float&' from an rvalue of type 'float'
                           //(有些编译器可以成功编译该语句,但会给出一个warning) 
    float c=fn2(5.0);//case 3:返回引用
    float &d=fn2(5.0);//case 4:用函数返回的引用作为新引用的初始化值
    cout<<a<<endl;//78.5
    //cout<<b<<endl;//78.5
    cout<<c<<endl;//78.5
    cout<<d<<endl;//78.5
    return 0;
}

case1:用返回值方式调用函数

如下图:

返回全局变量temp的值时,c++会在内存中创建临时变量并将temp的值拷贝给该临时变量。当返回到主函数main后,赋值语句a=fn1(5.0)会把临时变量的值再拷贝给变量a

case2:用函数的返回值初始化引用的方式调用函数

如下图:

这种情况下,函数fn1()是以值方式返回到,返回时,首先拷贝temp的值给临时变量。

返回到主函数后,用临时变量来初始化引用变量b,使得b成为该临时变量到的别名。

由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b=fn1(5.0);之后),所以b面临无效的危险,很有可能以后的值是个无法确定的值。

 如果真的希望用函数的返回值来初始化一个引用,应当先创建一个变量,将函数的返回值赋给这个变量,然后再用该变量来初始化引用:

  int x=fn1(5.0);
  int &b=x;

case3:用返回引用的方式调用函数

如下图:

这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数,即主函数的赋值语句中的左值是直接从变量temp中拷贝而来(也就是说c只是变量temp的一个拷贝而非别名) ,这样就避免了临时变量的产生。尤其当变量temp是一个用户自定义的类的对象时,这样还避免了调用类中的拷贝构造函数在内存中创建临时对象的过程,提高了程序的时间和空间的使用效率。

case4:用函数返回的引用作为新引用的初始化值的方式来调用函数

如下图:

这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数。

1.在主函数中,一个引用声明d用该返回值初始化,也就是说此时d成为变量temp的别名。

2.由于temp是全局变量,所以在d的有效期内temp始终保持有效,故这种做法是安全的。

3.不能返回局部变量的引用。如上面的例子,如果temp是局部变量,那么它会在函数返回后被销毁,此时对temp的引用就会成为“无所指”的引用,程序会进入未知状态。

4.不能返回函数内部通过new分配的内存的引用。虽然不存在局部变量的被动销毁问题,但如果被返回的函数的引用只是作为一个临时变量出现,而没有将其赋值给一个实际的变量,那么就可能造成这个引用所指向的空间(有new分配)无法释放的情况(由于没有具体的变量名,故无法用delete手动释放该内存),从而造成内存泄漏。因此应当避免这种情况的发生

5当返回类成员的引用时,最好是const引用。这样可以避免在无意的情况下破坏该类的成员。

6.可以用函数返回的引用作为赋值表达式中的左值

#include<iostream>
using namespace std;
int value[10];
int error=-1;
int &func(int n){
    if(n>=0&&n<=9)
        return value[n];//返回的引用所绑定的变量一定是全局变量,不能是函数中定义的局部变量 
    else
        return error;
}
 
int main(){
    func(0)=10;
    func(4)=12;
    cout<<value[0]<<endl;
    cout<<value[4]<<endl;
    return 0; 
}

用引用实现多态

在C++中,引用是除了指针外另一个可以产生多态效果的手段

也就是说一个基类的引用可以用来绑定其派生类的实例

class Father;//基类(父类)
class Son:public Father{.....}//Son是Father的派生类
Son son;//son是类Son的一个实例
Father &ptr=son;//用派生类的对象初始化基类对象的使用

特别注意:

ptr只能用来访问派生类对象中从基类继承下来的成员。如果基类(类Father)中定义的有虚函数,那么就可以通过在派生类(类Son)中重写这个虚函数来实现类的多态。

函数中返回引用和返回值的区别

主要讨论下面两个函数的区别

int& at()
{
    return m_data_;
}
int at()
{
    return m_data_;
}

上面两个函数,第一个返回值是int的引用int&,第二个返回值是int,二者的区别是什么呢?

我们先用一个语句 const int& a = mymay.at(); 来分别调用一次上面两个函数,然后看汇编语言的结果。

反汇编结果:

#int& at()
#{
#    return m_data_;
#}
00BB6830  push        ebp
00BB6831  mov         ebp,esp
00BB6833  sub         esp,0CCh
00BB6839  push        ebx
00BB683A  push        esi
00BB683B  push        edi
00BB683C  push        ecx
00BB683D  lea         edi,[ebp-0CCh]
00BB6843  mov         ecx,33h
00BB6848  mov         eax,0CCCCCCCCh
00BB684D  rep stos    dWord ptr es:[edi]
00BB684F  pop         ecx
00BB6850  mov         dword ptr [this],ecx
        m_data_++;
00BB6853  mov         eax,dword ptr [this]
00BB6856  mov         ecx,dword ptr [eax]
00BB6858  add         ecx,1
00BB685B  mov         edx,dword ptr [this]
00BB685E  mov         dword ptr [edx],ecx
        return m_data_;
#取地址this中的值5879712(m_data_的地址)到寄存器eax中,此时寄存器eax存的是m_data_的地址
00BB6860  mov         eax,dword ptr [this]
    }
00BB6863  pop         edi
00BB6864  pop         esi
00BB6865  pop         ebx
00BB6866  mov         esp,ebp
00BB6868  pop         ebp
00BB6869  ret
    const int& a = mymay.at();
00176AA2  lea         ecx,[mymay]
00176AA5  call        MyMat::at (0171546h)
#此时寄存器eax中的值为m_data_的地址5879712,直接将地址5879712存入地址a中。
00176AAA  mov         dword ptr [a],eax
    cout << a << endl;
#int at()
#{
#    return m_data_;
#}
012B6830  push        ebp
012B6831  mov         ebp,esp
012B6833  sub         esp,0CCh
012B6839  push        ebx
012B683A  push        esi
012B683B  push        edi
012B683C  push        ecx
012B683D  lea         edi,[ebp-0CCh]
012B6843  mov         ecx,33h
012B6848  mov         eax,0CCCCCCCCh
012B684D  rep stos    dword ptr es:[edi]
012B684F  pop         ecx
012B6850  mov         dword ptr [this],ecx
        return m_data_;
#和上面一样,也是先取出m_data_的地址
012B6853  mov         eax,dword ptr [this]
#和上面不一样,不是直接将m_data_的地址放入寄存器eax中,而是取地址5879712中的值(m_data_=3)放入寄存器eax中,此时寄存器eax存的是m_data_的值(3)
012B6856  mov         eax,dword ptr [eax]
    }
012B6858  pop         edi
012B6859  pop         esi
012B685A  pop         ebx
012B685B  mov         esp,ebp
012B685D  pop         ebp
012B685E  ret
    const int& a = mymay.at();
008E6AA2  lea         ecx,[mymay]
008E6AA5  call        MyMat::at (08E154Bh)
#此时eax的值为3,将3存入地址ebp-24h中,
008E6AAA  mov         dword ptr [ebp-24h],eax
#将eax的值变成ebp-24h
008E6AAD  lea         eax,[ebp-24h]
#将地址ebp-24h写到地址为a中,此时a代表的地址是ebp-24h
008E6AB0  mov         dword ptr [a],eax
    cout << a << endl;

所以结论就是:

1、返回值为引用型(int& )的时候,返回的是地址,因为这里用的是 int& a=mymay.at(); ,所以a和m_data_指的是同一块地址(由寄存器eax传回的5879712)。

2、返回值不是引用型(int)的时候,返回的是一个数值。这个时候就很有意思了,编译器是先将这个数值放入一个内存中(上面例子中,该内存地址为ebp-24h),再把这个地址付给a,此时的a代表的地址是ebp-24h,和m_data_代表的地址不一样(m_data_代表的地址是5879712)。

3、综上两点可以看出,当返回的值不是引用型时,编译器会专门给返回值分配出一块内存的(例子中为ebp-24h)。

说明一下函数返回时

如果不是返回一个变量的引用,则一定会生成一个临时变量。

看下面的函数,返回的是t而不是&t,所以一定会有临时变量产生。

 T function1(){
     T t(0);
     return t;
 }
 T x=function1();

这里的过程是:

1.创建命名对象t

2.拷贝构造一个无名的临时对象,并返回这个临时对象

3.由临时对象拷贝构造对象x

4.T x=function1();这句语句结束时,析构临时对象

这里一共生成了3个对象,一个命名对象t,一个临时对象作为返回值,一个命名对象x。

下面的函数稍微复杂一定,它没有先定义一个中间变量t,看起来似乎是直接返回了一个临时变量。但实际上,如果不经过c++的优化,那么它并没有提高效率,因为它还是创建了3个对象。

 T function2(){
      return T(0);
 }
 T x=function2();

这里的过程是:

1.创建一个无名对象

2.由无名对象拷贝构造一个无名的临时对象

3.析构无名对象,返回临时对象

4.由临时对象拷贝构造对象x

5.T x=function2()语句结束时,析构临时对象。

这里一共生成了3个对象,其中有2个对象都是马上被析构掉的,不能被后面的代码使用。既然是这样,那么就会有优化的余地,可以尝试着不要前面的两个临时变量。c++确实会做这样的优化,优化后的c++会避免匿名对象和临时对象这两个对象的生成,而直接生成x,这样就减少了两次对象生成-回收的消耗,提高了程序性能。

其实function1()这段代码也是会经过优化的,但因为临时对象t是一个命名对象,所以一定会被创建。存储返回值的临时对象是多余的,会被优化掉而不生成。

但是,程序员不应该依赖这种优化,因为c++不保证这种优化一定会做。

以上为个人经验,希望能给大家一个参考,也希望大家多多支持编程网。

--结束END--

本文标题: C/C++ 引用作为函数的返回值方式

本文链接: https://lsjlt.com/news/154255.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • C/C++ 引用作为函数的返回值方式
    目录case1:用返回值方式调用函数case2:用函数的返回值初始化引用的方式调用函数case3:用返回引用的方式调用函数case4:用函数返回的引用作为新引用的初始化值的方式来调用...
    99+
    2024-04-02
  • C++ 函数如何返回引用或指针作为返回值?
    c++++ 函数可以通过以下方式返回引用或指针作为返回值:返回引用:使用 '&amp;' 作为返回类型,允许函数修改调用者对象的值。返回指针:使用 '*'' 作为返回类型,允许函...
    99+
    2024-04-13
    指针 引用 c++
  • C++ 函数指针作为函数返回值
    函数指针可以作为函数返回值,允许我们在运行时确定要调用的函数。语法为:returntype (*function_name) (param1, param2, ...)。优点包括动态绑定...
    99+
    2024-04-14
    返回值 函数指针 c++
  • C语言函数多个返回值方式
    目录函数返回多个值下面附上小小的demo函数中有多个return?C语言中一个函数可以有几个返回值?总结函数返回多个值 在平时项目中,我们会遇到一个函数要返回多个值的问题。 但是c语...
    99+
    2023-02-24
    C语言函数 多个返回值 C语言函数返回值
  • 理解 C++ 函数返回值类型的作用域
    c++++ 函数返回值类型的作用域仅限于函数体内,用于指定函数返回的值的数据类型,有助于确保类型安全和正确使用返回的值。 理解 C++ 函数返回值类型的作用域 在 C++ 中,函数的返...
    99+
    2024-04-13
    函数 c++ 返回值 作用域
  • C++ 函数的参数和返回值
    函数在 c++++ 中通过参数传递数据并通过返回值返回信息:参数:函数定义中声明,允许函数接收外部值。返回值:函数定义中声明,使函数能够将信息返回给调用代码。 C++ 函数的参数和返回...
    99+
    2024-04-12
    参数 函数 c++
  • 函数返回值在 C++ 中的用途
    在 c++++ 中,函数可以通过 return 关键字返回值,可以是任何数据类型,包括数值和对象。返回值用于将信息或计算结果传给调用方,可用于检查错误、优化性能,并实现单一职责原则。 ...
    99+
    2024-04-30
    c++ 函数返回值 关键词
  • C++中的函数返回值问题
    目录1、返回值2、指针类型的函数——返回指针3、返回引用4、综合示例首先,强调一点,和函数传参一样,函数返回时也会做一个拷贝。 从某种角度上看,和传参一样,也...
    99+
    2024-04-02
  • C++函数返回数据指针的方式
    本篇内容介绍了“C++函数返回数据指针的方式”的有关知识,在实际案例的操作过程中,不少人都会遇到这样的困境,接下来就让小编带领大家学习一下如何处理这些情况吧!希望大家仔细阅读,能够学有所成!三个函数,返回数组指针,效果是一样的using a...
    99+
    2023-06-04
  • Shell函数返回值方式
    目录1、返回值的方式:2、return和echo使用场景区别:3、场景示例1、返回值的方式: 方法一:return方法二: echo 2、return和echo使用场景区别: (1).使用return返回值: 使用ret...
    99+
    2022-08-08
    Shell函数返回值 Shell返回值
  • C++11 成员函数作为回调函数的使用方式
    目录C++11成员函数作为回调函数使用示例1示例2类成员函数作为回调函数的方法及注意点类成员函数当回调函数的方法为什么回调函数必须为静态函数?类的静态成员函数如何访问非静态成员?C+...
    99+
    2022-11-13
    C++11 成员函数 C++11 回调函数 使用回调函数
  • C# 函数返回多个值的方法详情
    目录引言1.使用ref参数2.使用out参数修饰符3. 使用元组类4.使用C#7 ValueTuple5. 使用结构或类引言 根据 C# 语言规范,不可能从一个方法返回多个值。使用 ...
    99+
    2024-04-02
  • C++ 函数返回值类型在多态性中的作用
    多态中,函数返回值类型规定了当派生类重写基类方法时,返回的具体对象类型。派生类方法的返回值类型可以与基类相同或更具体,允许返回更派生的类型,从而提高灵活性。 C++ 函数返回值类型在多...
    99+
    2024-04-13
    多态性 函数返回值类型 c++
  • C++ 函数返回值:速查常见的返回码含义
    c++++ 函数的返回码用于表示操作的结果,常见返回码含义包括:0:操作成功1:操作失败-1:内存分配失败-2:文件打开失败-3:参数不正确-4:资源不足-5:无效指针 C++ 函数返...
    99+
    2024-04-29
    c++ 函数返回值
  • c++函数返回引用的情况有哪些
    C++函数返回引用的情况有以下几种: 返回左值引用:函数可以返回已存在的变量、类成员或者数组的引用。例如: int& getVar...
    99+
    2023-10-23
    c++
  • C++中stack的pop()函数返回值解析
    目录stack的pop()函数返回值全部demo分析C++的返回值优化从函数返回值RVOstack的pop()函数返回值 int temp = s.pop(); co...
    99+
    2024-04-02
  • 在c++中,什么叫函数的返回值
    在 c++ 中,函数只能返回一个值。解决方法:引用传递、结构体或类、out 参数。没有返回值的函数可以使用 void 类型,表示不返回任何值。 什么是 C++ 中函数的返回值? 在 C...
    99+
    2024-05-14
    c++
  • C#函数out多个返回值问题
    目录C#函数返回多个参数数值C#调用一个函数通过out返回多个变量值/数据举例总结C#函数返回多个参数数值 通过out/Ref实现,声明函数时用out指定返回变量。 写了一个DEMO...
    99+
    2023-02-24
    C#函数 C# out C#多个返回值
  • golang函数返回值的传递方式
    go 函数的返回值采用值传递方式,即函数内对返回值的修改不会影响调用代码中的值。然而,通过指针传递,我们可以传递变量的内存地址,以便对指针的修改反映在调用代码的原始变量中。这种方式常用于...
    99+
    2024-04-23
    返回值 传递方式 golang
  • c语言怎么调用python函数返回值
    在c语言中调用python函数返回值,具体方法如下:void test1() { Py_Initialize();//初始化python getcurrent(); PyObject *pModule = NULL, *pFunc = NU...
    99+
    2024-04-02
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作