C++ 返回引用与返回值区别
近期在跟着up黑马程序员学习C++课程,昨天对于链式调用产生了一些疑问,具体疑问如下
- 带有返回类型的return的返回到底是什么机制
- 返回"原有数值类型引用"与返回“原有数值类型”有什么不同
下面我讲针对这两个问题逐步讲解,希望自己一边写这篇博客同时缕清思绪
以下可能会有一些自己的错误理解,还请大家能够指出,由于我还没关注汇编,所以本篇文章会略长,也只是浅层分析。
一、return返回机制
这里需要先引出计算机的区概念
区 | 功能 |
---|---|
代码区 | 存放函数体的二进制代码,由系统进行管理 |
全局区 | 存放全局变量和静态变量及常量 |
栈区 | 由编译器自动分配和释放,存放函数的参数值,局部变量 |
堆区 | 由程序员分配和释放,若不释放则在程序结束由系统释放 |
这里着重对栈区进行讲解,对于编译器自动分配和释放我们可以理解为当编译器执行完某一个函数之后,会对这个函数中的局部变量所占据的存储空间进行释放,下面我举个实例
void main()
{//设置指针int* p = NULL;p = Test();cout << "p指向的地址为" << p << endl;cout << "p指向的地址所对应的数值为" << *p << endl;}int* Test()
{int a = 10;cout << "a的地址为" << &a << endl;return &a;
}
从结果中我们可以看到在Test函数中局部变量a的地址的确赋值给p了,二者相等,但是意外出现在最后一个输出,此时我们调取p指针指向的地址所对应数值不是10,原因在于在运行完Test函数后变量a所占据的栈区空间已经被清掉。
如果需要显示10的话则需要将变量a调入全局区或者堆区
static int a = 10;//调入全局区
int *a =new int(10);//调入堆区
通过上述案列我们知道局部变量在所在函数运行完后会被释放,这时候问题来了,如果Test函数传入一个变量,那局部函数中是变量本体吗?从上述堆区的特点中我们发现如果是变量本体,那肯定把本体清掉,所以传入的不是变量本体,示例如下
void main()
{//设置变量int a = 10;//设置指针int* p = NULL;p = Test(a);cout << "a的地址为" << &a << endl;cout << "Test中的形参地址为" << p << endl;}int* Test(int p)
{return &p;
}
所以这个变量是是一个本体变量克隆体,这个克隆体变量数值与本体一致但是存储空间是编译器另外开辟的。
大家可以利用借助这个概念去理解去理解return返回的其实也是一个克隆体,我目前没有一个很好的办法去展现这个机制(大家也可以参考二中的第一个案列,只不过二中是结论性的展示),详细机制可以参考这篇文章,利用汇编语言向大家展示了这个过程,这篇汇编语言中直接向大家展示了return 返回时会先将返回值存储起来,强烈推荐!!
http://t.zoukankan.com/wgang171412-p-5019148.html
二、返回"原数值类型引用"与“原数值类型”不同
在解释这个问题之前我直接先向大家展示一个案列
class Test
{public://构造函数Test(int mem){this->mem = mem;}//加法函数Test Test_ADD(const Test &P){this->mem = P.mem + this->mem;//this代表指向当前对象的指针return *this;//*this为指向的对象的本体}//析构函数~Test(){}int mem;};void main()
{Test P1 = Test(10);Test P2 (P1);P2.Test_ADD(P1).Test_ADD(P1);cout <<"当前P2的数值为" << P2.mem << endl;}
对于函数为什么输出20而不是30原因在这里:
在返回后实际上整个整体变成了P2’,这也是输出20的原因,那如何返回P2输出30呢,这里我们要想一下关于return返回的形式,例如这段代码
P= Test();
int &Test()
{int a=10;return a;
}
简明表示为
int &P=克隆体(a);
引用跟指针常量表示不同,这里面的P只是克隆体a的别名,数值与克隆体a相同,且存储空间也不会新开辟,即就是克隆体a的空间,但是在底层逻辑中二者又是相同的,即在底层引用变量由指针按照指针常量的方式实现,只是操作不同,具体可以参考
https://www.zhihu.com/question/37608201/answer/545635054
利用指针常量表示会让我们更好的去了解原理,在案列程序里面我们知道*this为对象本体,表示为
Test P2.Test_ADD(P1) = 克隆体(*this)
此时P2.Test_ADD(P1)有着与新的P2相同的数值,但是所在的地址不同,既然所在地址不同,则Class P2.Test_Add就变成了一个新的变量。
那如果返回类型是Test &呢
//加法函数Test &Test_ADD(const Test &P){this->mem = P.mem + this->mem;//this代表指向当前对象的指针return *this;//*this为指向的对象的本体}
这时候我们可以表示为
Test * const P2.Test_ADD(P1) = 克隆体(&(*this))
注意,这时候虽然返回的是个克隆体,但是返回的是一个地址形式,而且这个地址与本体P2相同,这表示P2.Test_ADD(P1)为指向P2的指针,但是由于指针与引用操作逻辑不同,故P2.Test_ADD(P1)为返回的本体P2。
所以牢记住这个结论:
在底层逻辑中引用返回的是变量的地址,切勿在某个函数对其内部的局部变量进行引用返回,虽然地址仍在,但是地址内部内容位于栈区,已经被编译器自动清掉。
具体参考本篇文章
https://www.cnblogs.com/JMLiu/p/7928425.html
本篇只是在个人的理解去讲述给自己的,方便以后以后的学习,而且对于返回引用类型可以牢牢记住返回的是一个地址就可以了,有什么错误还请大家指出。
C++ 返回引用与返回值区别相关推荐
- C++函数中返回引用和返回值的区别
https://www.cnblogs.com/JMLiu/p/7928425.html 转载于:https://www.cnblogs.com/liuw-flexi/p/10745246.html
- C++类成员函数返回对象及返回引用
C++类函数返回类的对象及类的引用 一.代码例题 二.返回引用 三.返回对象 一.代码例题 #include <iostream> using namespace std;class ma ...
- java中数组的返回值是什么类型_java基础学习:数组的常用操作与基础二维数组用法、及基本数据类型和引用数据类型赋值的区别...
一.Arrays jdk中为了便于开发,给开发者提供了Arrays类,其中包含了很多数组的常用操作.例如快速输出.排序.查找等. 1.数组的常用方法 1.数组以字符串形式的输出,2.sort对数组排序 ...
- C++基础08-this指针-const修饰成员函数-函数返回引用/值
一.this指针 1.C++类对象中的成员变量和成员函数是分开存储的.C语言中的内存四区模型仍然有效! 2.C++中类的普通成员函数都隐式包含一个指向当前对象的this指针. 3.静态成员函数.成员变 ...
- 函数返回值 返回引用
先看看该链接讨论的问题作为热身.http://topic.csdn.net/u/20070616/13/2a6a1739-1a69-4829-a90f-fdb19a61e095.html 函数返回值和 ...
- 2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码)
2020-09-22C++学习笔记之引用1(1.引用(普通引用)2.引用做函数参数 3.引用的意义 4.引用本质5.引用结论 6.函数返回值是引用(引用当左值)7测试代码) 1.引用(普通引用) 变量 ...
- C++返回引用,使用引用接收和非引用接收的区别
当函数返回值类型为引用时,一般就用引用类型去接收,或者就使用了引用的作用,如果用非引用类型接受,就等于将函数返回的引用的数据值,复制给了该接收对象,和函数返回非引用类型是一样的效果. 但是,vs201 ...
- 返回值与返回引用的问题
此处小论一下返回值与返回引用的问题. 先看程序: #include <iostream> using namespace std;class X {int i; public:X(int ...
- 函数返回值是否使用引用类型的问题:理解引用、返回值
在<对象更有用的玻璃罩--常引用>一文中,介绍了对象作为函数的参数时,推荐使用引用的形式.并且,如果实际参数的值不允许改变时,声明为常引用更佳. 在<第8周-任务1-方案3-复数类中 ...
最新文章
- CSS之布局(盒模型)
- “AI”战疫在行动,一文盘点百度大脑增援疫情防控的AI操作
- 企业微服务中台落地实践和思想之我见
- java数列求和_java中关于数列求和的计算方法
- RPM安装rabbitMQ
- vuex 的 action 传参问题
- django新建一个项目_如何使用Django创建项目
- java resource放入的文件没有生成在classes中_JAVA程序员学习笔记----mybatis深入剖析...
- 5.Linux/Unix 系统编程手册(上) -- 深入探究文件IO
- NOIP-质因数分解
- 2020年全国大学生数学建模B题题目概要
- 计算机保持在线的几种方法,获取网络电影实际地址的几种方法.doc
- 用python绘制叠加等边三角形_python 叠加等边三角形的绘制
- Modbus协议应用纪实
- 开发微信小程序都需要哪些资质?
- 177本名著浓缩成了177句话!别等自己做错时才明
- 定制石墨相氮化碳量子点(C3N4-R),g-C3N4量子点修饰的MoO3/TiO2复合膜,Mn掺杂ZnS量子点,核壳结构的绿光 InP/ZnS量子点
- VS2010 正式版下载链接
- Charles切环境,使用map Local
- kettle 提交数据量_基于kettle工具提高表输出写入速度(每秒万条记录)
热门文章
- python月球和地球体重_地球和月球围绕太阳运行,与Python玩游戏
- Day8 每日打卡 -简单可预期
- 存储管理——页式存储管理
- 【特色小镇】智慧小镇:小镇建设总体规划方案
- 六个在线生成网址二维码的API接口
- 非匀质化资金池——为什么资产 NFT 化是 DeFi 的必经之路
- 2017大数据产业链的大变化:数据应用仍在奋斗 淘金路上见曙光
- 临近面试却还没准备好?大厂内部面试官权威的Android面试指导,带你弯道超车
- 语文课内外杂志语文课内外杂志社语文课内外杂志社2022年第14期目录
- 安装gin失败 # cd .; git clone -- https://github.com/gin-gonic/gin xcrun: error: invalid active develope