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

2023-10-27

(1)以引用返回函数值,定义函数时需要在函数名前加 &

(2)用引用返回一个函数值的最大好处是,在内存中不产生被返回值的副本。

引用作为返回值,必须遵守以下规则:

  • (1)不能返回局部变量的引用。主要原因是局部变量会在函数返回后被销毁,因此被返回的引用就成为了"无所指"的引用,程序会进入未知状态。
#include <iostream>
using namespace std;

int& test1()
{
	int n = 5;
	return n;
}
 
int main()
{
	int i = test1();
	cout << i << endl;  

	return 0;
}

g++ 5.4.0 编译报警告,运行报段错误!

VS2015编译报:warning C4172: 返回局部变量或临时变量的地址: n

但可正常运行输出

因为局部变量n赋值给i时还没有被新数据覆盖。

由此可看出g++更加严谨,不能返回局部变量的引用!

补充:

#include <iostream>
using namespace std;

int& test1()
{
	int n = 5;
	return n;
}

int main()
{
	// int i = test1();   //  因为有拷贝操作,g++5.4.0运行将会报段错误
	int &i = test1();  //  因为无拷贝操作,g++5.4.0运行将不会报错
	// cout << i << endl; // g++5.4.0运行将会报段错误
	return 0;
}

再举1例:

#include <iostream>
using namespace std;

int& test1()
{
	int n = 5;
	return n;
}


void test2()
{
	int b = 8;
}


  
int main()
{

	int &i = test1();
	cout << i << endl;  // 输出 5

	int &b = test1();
	 
	cout << b << endl;  // 输出 5
	test2();
	cout << b << endl;  // 输出 8

	int c = test1();
	test2();
	cout << c << endl;  // 输出 5

	return 0;

}

VS2015编译报警告:warning C4172: 返回局部变量或临时变量的地址: n

但可正常运行输出:

 

i实际为test1 中局部变量的别名, 当test1退出时,i 指向的内存中值未被覆盖,直接输出为5;
输出第二个b 之前,因为调用了test2, 实际指向的内存空间已被覆盖为8,故输出为8;
c是一个新的变量,其值为5, 故输出为5;

g++ 5.4.0编译警告,运行报段错误:

  •  (2)不能返回函数内部new分配的内存的引用。虽然不存在局部变量的被动销毁问题,可对于这种情况(返回函数内部new分配内存的引用),又面临其它尴尬局面。例如,被函数返回的引用只是作为一 个临时变量出现,而没有被赋予一个实际的变量,那么这个引用所指向的空间(由new分配)就无法释放,造成memory leak。
  •  (3)可以返回类成员的引用,但最好是const。主要原因是当对象的属性是与某种业务规则(business rule)相关联的时候,其赋值常常与某些其它属性或者对象的状态有关,因此有必要将赋值操作封装在一个业务规则当中。如果其它对象可以获得该属性的非常 量引用(或指针),那么对该属性的单纯赋值就会破坏业务规则的完整性。

1. 用返回值方式调用函数

#include <iostream>
using namespace std;

int g_a; // 定义全局变量g_a

int test() {
	g_a = 168;
	return g_a;
}

int main()
{
	int i = test();
	
	cout << i << endl; 
	return 0;
}

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

2. 用函数的返回值初始化引用变量

#include <iostream>
using namespace std;

int g_a; // 定义全局变量g_a

int test() {
	g_a = 168;
	return g_a;
}

int main()
{
	int &i = test();
	
	cout << i << endl; 
	return 0;
}

编译报错:error C2440: “初始化”: 无法从“int”转换为“int &”

注:(有些编译器可以成功编译,但会给出一个warning)

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

#include <iostream>
using namespace std;

int g_a; // 定义全局变量g_a

int test() {
	g_a = 168;
	return g_a;
}

int main()
{
	const int &i = test();

	cout << i << endl; 
	return 0;
}

 虽然编译运行通过,但该问题依然存在!

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

int b = test();
int &i = b;

3. 用返回引用的方式调用函数

#include <iostream>
using namespace std;

int g_a; // 定义全局变量g_a

int& test() {
	g_a = 168;
	return g_a;
}

int main()
{
	int i = test();

	cout << i << endl; 
	return 0;
}

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

4. 用函数返回的引用作为新引用变量的初始化值

#include <iostream>
using namespace std;

int g_a; // 定义全局变量g_a

int& test() {
	g_a = 168;
	return g_a;
}

int main()
{
	int &i = test();

	cout << i << endl; 
	return 0;
}

这种情况下,函数test()的返回值不产生副本,而是直接将变量g_a返回给主函数。在主函数中,引用变量i用该返回值初始化,也就是说此时i成为变量g_a的别名。由于g_a是全局变量,所以在i的有效期内g_a始终保持有效,故这种做法是安全的。

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

当函数返回一个引用时,则返回一个指向返回值的隐式指针。这样,函数就可以放在赋值语句的左边。例如,请看下面这个简单的程序:

#include <iostream>
using namespace std;

double vals[] = { 10.1, 12.6, 33.1, 24.1, 50.0 };

double& setValues(int i)
{
	return vals[i];   // 返回第 i 个元素的引用
}

// 要调用上面定义函数的主函数
int main()
{

	cout << "改变前的值" << endl;
	for (int i = 0; i < 5; i++)
	{
		cout << "vals[" << i << "] = ";
		cout << vals[i] << endl;
	}

	setValues(1) = 20.23; // 改变第 2 个元素
	setValues(3) = 70.8;  // 改变第 4 个元素

	cout << "改变后的值" << endl;
	for (int i = 0; i < 5; i++)
	{
		cout << "vals[" << i << "] = ";
		cout << vals[i] << endl;
	}
	return 0;
}

输出:

有兴趣可以看下:为什么 C++ 有指针了还要引用?

参考链接:http://www.runoob.com/cplusplus/returning-values-by-reference.html

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

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

随机推荐

  • js在控制台输出菱形

    js在控制台输出菱形 以一个上半部分10行 下半部分9行的为例 var str 在控制台输出要采用字符串拼接 所以先定义一个空字符串 for var row 1 row lt 10 row 外层循环控制行数 先输出上半部分的10行 for
  • HTTPOXY -- CGI 环境变量劫持漏洞分析

    0x00 前言 昨晚 一个名为 HTTPOXY 的漏洞在安全圈内广泛传播 云盾攻防对抗团队第一时间对此漏洞进行了深入分析 发现其本质是一个 CGI 环境变量劫持漏洞 对 CGI 的环境变量 HTTP PROXY 变量进行劫持 如果 CGI
  • Python作业

    提示 文章写完后 目录可以自动生成 如何生成可参考右边的帮助文档 文章目录 python安装步骤 打开官网 https www python org downloads windows 下载中心 测试安装是否成功 windows gt 运行
  • BurpSuite学习:在火狐浏览器使用foxyproxy添加代理127.0.0.1后无法正常上网

    个人认为 因为127 0 0 1就是本机上的本地服务器 它应该不具备服务器的功能 所以浏览器向本地服务器发送请求也不会转发到外网 更不会得到回应 打开burpsuite 它会承担服务器的作用转发请求 这时候浏览器会显示应该是请求被拦截之类的
  • 基于zinx的go tcp通信案例

    基于zinx的go tcp通信示例 一 zinx简介 https gitee com Aceld zinx Zinx是一个基于Golang的轻量级tcp服务框架 根据官方的定位 zinx是在游戏领域或者其他长链接的领域的轻量级企业框架 其使
  • Android RecyclerView对应的适配器中方法的执行顺序和具体作用详解

    前些天发现了一个蛮有意思的人工智能学习网站 8个字形容一下 通俗易懂 风趣幽默 感觉非常有意思 忍不住分享一下给大家 点击跳转到教程 1 代码的执行顺序为 首次进入会先调用getItemCount 返回条目的个数 之后会分别调用 getIt
  • 十大经典排序算法(动态演示+代码)-桶排序

    桶排序 桶排序 Bucket sort 或所谓的箱排序 是一个排序算法 工作的原理是将数组分到有限数量的桶里 每个桶再个别排序 有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序 最后依次把各个桶中的记录列出来记得到有序序列 桶排
  • Qt中解除按键在聚焦时键盘空格键可以控制

    Qt中解除按键在聚焦时键盘空格键可以控制 只允许鼠标左键可以点击按键 首先需要知道原因 为什么键盘上的空格键也可以点击按键 根据Qt官网说明 A push button emits the signal clicked when it is
  • vue 前端导出excel xlsx插件 修改表格行高列宽度

    cnpm install xlsx save import XLSX from xlsx 列表页引入 表格导出 handleExport if this tableData total 0 this message error 暂无数据 t
  • SpringBoot 对接微信公众号模板消息通知

    效果图见测试结果 有问题评论 模板消息 开通模板消息功能 登录微信公众平台开通模板消息功能 未开通时 添加模板消息 审核通过后 登录微信公众平台 点击 广告与服务 gt 模板消息 公众号如果未设置服务类目 则需要先设置服务类目 然后在模板库
  • html通过id设置css样式,使用ID值如何应用CSS样式?

    假设你一个ID为 mytext 的HTML段落标记 如下面的代码片段所示 This is HTML interview questions 你可以使用有着 id 名称的 选择器创建一个样式 并应用CSS值到段落标记 为了应用样式到 myte
  • [spring] 注解@Autowired是如何实现的

    Table of Contents 前言 Autowired注解用法 Autowired注解的作用到底是什么 Autowired注解是如何实现的 例子注解 Override 自己实现一个注解 Autowired注解实现逻辑分析 问题 注解的
  • LaTeX学习笔记(数学公式编辑:公式与矩阵中符号的设置)

    1 使用 left与 right指令对公式中括号的长度进行调整 方法 将 left与 right命令分别放到公式左右括号前 这是调整括号长度前 这是调成括号长度后 2 使用 left与 right指令对公式中竖线的长度进行调整 方法 在数学
  • python怎么做多个矩阵,在python中添加两个矩阵

    I m trying to write a function that adds two matrices to pass the following doctests gt gt gt a 1 2 3 4 gt gt gt b 2 2 2
  • Java GUI,mybatis实现资产管理系统

    Java GUI 资产管理系统 前言 为了做java课设 学了一手Java GUI 感觉蛮有意思的 写写文章 做个视频记录一下 欢迎大家友善指出我的不足 资产管理系统录制视频 从头敲到尾 模块划分 资产信息管理 资产信息查询 各种条件查询
  • 实现一个完整的前后端交互

    题目一 计算器 1 构建项目 前端代码
  • C/C++中float和double的存储结构(转)

    在C C 中float是32位的 double是64位的 两者在内存中的存储方式和能够表示的精度均不同 目前C C 编译器标准都遵照IEEE制定的浮点数表示法来进行float double运算 无论是float还是double 在内存中的存
  • 软件测试需要学什么?软件测试技能图谱

    很多新手 不知道软件测试学习该如何开始 软件测试需要掌握哪些知识 下面是根据本人的理解 粗略整理的一个学习大纲 基本上涵盖了软件测试工程师需要掌握的全部技能 希望对刚入行或者准备学习测试的朋友提供一点指引 1 测试基础理论 不管有没有计算机
  • docker 搭建 Jenkins 容器 ,拉取git代码

    Docker 搭建 Jenkins 容器 拉取git代码 一 安装jenkins 前提条件 宿主机安装docker 1 拉取镜像 docker pull jenkins jenkins 2 查看镜像 docker images 列表中有je
  • C++ 引用作为函数返回值

    1 以引用返回函数值 定义函数时需要在函数名前加 2 用引用返回一个函数值的最大好处是 在内存中不产生被返回值的副本 引用作为返回值 必须遵守以下规则 1 不能返回局部变量的引用 主要原因是局部变量会在函数返回后被销毁 因此被返回的引用就成