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

2023-11-20

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

特别注意:

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;
}

case 1:用返回值方式调用函数(如下图,图片来源:伯乐在线):

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

case 2:用函数的返回值初始化引用的方式调用函数(如下图,图片来源:伯乐在线)

这种情况下,函数fn1()是以值方式返回到,返回时,首先拷贝temp的值给临时变量。返回到主函数后,用临时变量来初始化引用变量b,使得b成为该临时变量到的别名。由于临时变量的作用域短暂(在C++标准中,临时变量或对象的生命周期在一个完整的语句表达式结束后便宣告结束,也就是在语句float &b=fn1(5.0);之后) ,所以b面临无效的危险,很有可能以后的值是个无法确定的值。

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

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

 case 3:用返回引用的方式调用函数(如下图,图片来源:伯乐在线)

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

case 4:用函数返回的引用作为新引用的初始化值的方式来调用函数(如下图,图片来源:伯乐在线)

这种情况下,函数fn2()的返回值不产生副本,而是直接将变量temp返回给主函数。在主函数中,一个引用声明d用该返回值初始化,也就是说此时d成为变量temp的别名。由于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; 
}

D.用引用实现多态

在C++中,引用是除了指针外另一个可以产生多态效果的手段。也就是说一个基类的引用可以用来绑定其派生类的实例

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

特别注意:

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

 

 
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++ 引用作为函数的返回值 的相关文章

随机推荐

  • windows下配置Mysql-5.7.9服务

    第一步 从官方网站下载 mysql 5 7 9 winx64 zip 第二步 解压缩 在根目录下复制my default ini 改名为my ini 第三步 初始化mysql目录 bin mysqld initialize user mys
  • 在渗透测试中,扫描器原理是什么

    在渗透测试中 扫描器原理是什么 渗透测试中的扫描器是一种自动化工具 用于识别目标系统中的漏洞 弱点或配置错误 扫描器通过发送特定的网络请求或使用其他技术手段来检查目标系统的安全性 并生成报告以供分析和修复 以下是扫描器的一般原理 1 信息收
  • 一眼看懂promise与async await的区别

    promise方法 let p1 new Promise resolve reject gt setTimeout gt resolve 我是p1 4000 let p2 new Promise resolve reject gt setT
  • 12.HTML5下一代的HTML标准介绍与初识尝试

    关注回复 学习交流群 加入 安全开发运维 答疑交流群 请朋友们 多多点击文中的广告 支持作者更新更多文章 目录 本文为作者原创文章 为尊重作者劳动成果禁止非授权转载 若需转载请在 全栈工程师修炼指南 公众号留言 或者发送邮件到 master
  • 运维之Linux发行版和容器镜像网站及开源软件收集

    关注 WeiyiGeek 公众号 将我设为 特别关注 每天带你玩转网络安全运维 应用开发 物联网IOT学习 0x00 概述 0x01 镜像源网站 国内镜像 国内高校 0x02 发行版官网 CentOS kail Debian Ubuntu
  • 客户机操作系统已禁用 CPU。请关闭或重置虚拟机。解决方法

    今天在用VMware安装CentOS7报了这个错误 在网上找半天都没解决 最后换一个地址下的镜像就能正常安装了 Index of centos 7 9 2009 isos x86 64
  • 12_Linux ARM架构_安装JDK8-银河麒麟V10(Kylin Linux Advanced Server V10 )操作系统

    12 Linux ARM架构 安装JDK8 银河麒麟V10 Kylin Linux Advanced Server V10 操作系统 1 官网下载aarch64架构jdk包 2 linux服务器中创建java文件夹 方便后期快速寻找 3 将
  • DevC++如何改成中文?

    DevC 如何改成中文 1 点击Tools工具 2 选择环境选项 3 选择简体中文 4 点击确定
  • 深入理解Google Cast(一)基本概念

    什么是google cast google cast允许用户将手机上的内容投影到TV上 然后用户可以将手机作为遥控器来控制TV上的媒体播放 Google cast SDK用于扩展你的app 使其支持google cast功能 一个Cast
  • 图像验证码识别(九)——训练和识别

    前面讲到已经把所有的字符经过去干扰 分割和归一化得到标准大小的单个字符 接下来要做的就是识别验证码了 现在要做的基本上也就和OCR没什么区别了 因为得到的字符已经是尽可能标准的了 下面的识别分为两个步骤 第一步先是特征值的提取 第二步是SV
  • ROC曲线绘制原理及如何用SPSS绘制ROC曲线

    本文同步发布于 脑之说 微信公众号 欢迎搜索关注 ROC曲线 Receiver operating characteristic curve 即受试者工作特征曲线 主要用来评价某个指标对两类被试 如病人和健康人 分类 诊断的效果 以及寻找最
  • 深入探索并发编程系列(五)-将内存乱序逮个正着

    当用C C 编写无锁代码时 一定要小心谨慎 以保证正确的内存顺序 不然的话 会发生一些诡异的事情 Intel在x86 x64体系结构手册的Volume 3 8 2 3 中列出了一些可能会发生的诡异的事情 这里介绍其中一个最简单的例子 假设在
  • autoware警告The ‘state_publisher‘ executable is deprecated. Please use ‘robot_state_publisher‘ instead

    在运行autoware官方demo autoware ai 的时候 启动my localization launch roslaunch autoware quickstart examples my localization launch
  • C++示例程序,演示如何将两个整数相加并打印结果

    include
  • iOS开发-国际化-配置App多语言

    作者 大慈大悲大熊猫 链接 http www jianshu com p 1edd4bda6fe5 來源 简书 著作权归作者所有 商业转载请联系作者获得授权 非商业转载请注明出处 基本设置 第一步 先在Project的info里添加所需要支
  • 使用Python对数据的操作转换

    1 列表加值转字典 在Python中 将列表的值转换为字典的键可以使用以下代码 myList name age location myDict k None for k in myList print myDict 输出 name None
  • 百度2012实习生校园招聘笔试题

    1 给一个单词a 如果通过交换单词中字母的顺序可以得到另外的单词b 那么b是a的兄弟单词 比如的单词army和mary互为兄弟单词 现在要给出一种解决方案 对于用户输入的单词 根据给定的字典找出输入单词有哪些兄弟单词 请具体说明数据结构和查
  • vue组件之间传值的几种方式

    这里写目录标题 vue组件传值 父传子 子传父 非父子组件传值 provide 和 inject 传值 事件总线传值 attrs listeners 使用 attrs listeners 进行子往上级传 vueX vue组件传值 父传子 父
  • Qt的基本语法及其使用(一)

    Qt的概念 Qt是通用的C 开发界面框架 C 图形用户界面 应用程序开发框架 既可以开发GUI程序也可以开发开发非GUI程序 Qt是面向对象的框架 使用特殊的代码生成扩展 Qt的历史 1991由QT公司研发 2008年被诺基亚收购 2012
  • C/C++ 引用作为函数的返回值

    语法 类型 函数名 形参列表 函数体 特别注意 1 引用作为函数的返回值时 必须在定义函数时在函数名前将 2 用引用作函数的返回值的最大的好处是在内存中不产生返回值的副本 代码来源 RUNOOB include