C++11特性之左值引用和右值引用

news2024/9/26 1:24:38

3.1二者的对比之内置类型

内置类型的无名对象(右值)为纯右值,其值本身不可改变

int main()
{
    int a=10;
    const int b=20;
    int& ra=a;//ok,左值引用
    const int& rb=b;//ok,常性左值引用
    
    const int& crv=30;//ok,也叫万能引用,即可以引用左值也可以引用右值
    /*上句实际上是变为:
    int tmp=30;
    const int& crv=tmp;*/
    int&& rv=30;//ok,右值引用
     /*上句实际上是变为:
    int tmp=30;
    int& rv=tmp;*/
    //常性左值引用和右值引用的区别:
    crv+=100;//error
    rv+=100;//ok,不是将30改为100,30为纯右值,不能改变。实际是将tmp的值改为100
    return 0;
}

3.2二者的对比之自己设计的类型

自己设计的类型的无名对象(右值)为将亡值,其值可以改变

class Int
{
	int value;
public:
	Int(int x = 0) :value(x) { cout << "create " << this << endl; }
	~Int() { cout << "destroy " << this << endl; }
	Int(const Int& it) :value(it.value)
	{
		cout << &it << " copy " << this << endl;
	}
	Int& operator=(const Int& it)
	{
		if (this != &it)
		{
			value = it.value;
		}
		cout << &it << " operator= " << this << endl;
		return *this;
	}
    void PrintValue()const
    {
        cout<<"value: "<<value<<endl;
    }
    Int& SetValue(int x)
    {
        value=x;
        return *this;
    }
};
Int fun(int x)
{
	Int tmp(x);
	return tmp;
}
int main()
{
    /*
    Int a(10);//左值
    const Int b(20);//常左值
    Int& ra=a;//左值引用
    const Int& rb=b;//常性左值引用
    */
    Int&& rv=Int(30);//右值引用,引用无名对象
    Int&& rf=fun(40);//引用函数返回值的将亡值对象
    rv.PrintValue();//ok
    rv.SetValue(300);//ok
    rv.PrintValue();
    rf.PrintValue();//ok
    rf.SetValue(400);//ok
    rf.PrintValue();
    return 0;
}

结果:

 

3.3将亡值的生存期问题

3.3.1使用无名对象作为例子:

int main()
{
    Int(10).PrintValue();
    cout<<"main end"<<endl;
    return 0;
}

结果:

 

将亡值在该行语句执行完后就会消亡,而不是等到主函数结束才消亡,其生存期就只在其调用点处。

当使用右值引用引用该无名对象(将亡值)时:

int main()
{
    Int&& rv=Int(30);
    rv.PrintValue();
    cout<<"main end"<<endl;
    return 0;
}

结果:

由此来看,使用右值引用去引用将亡值,该将亡值的生存期就会和右值引用的标识符相同。

3.3.2以函数返回值作为例子:

int main()
{
    fun(10).PrintValue();
    cout<<"main end"<<endl;
    return 0;
}

结果:

当使用右值引用引用该无名对象(将亡值)时:

 

int main()
{
    Int&& rf=fun(10);
    rf.PrintValue();
    rf.SetValue(100);
    rf.PrintValue();
    cout<<"main end"<<endl;
    return 0;
}

 结果:

与使用无名对象作为例子时的结果相同。

总结:将亡值在该行语句执行完后就会消亡,而不是等到主函数结束才消亡,其生存期就只在其调用点处。但是使用右值引用去引用将亡值,该将亡值的生存期就会和右值引用的标识符相同。

3.3.3将右值引用设置为静态,其生存期如何

int func()
{
	static Int&& rf=fun(10);//.data区
    rf.PrintValue();
    rf.SetValue(100);
    rf.PrintValue();
    cout<<"func end"<<endl;
    return 0;
}
int main()
{
    cout<<"main begin"<<endl;
    func();
    cout<<"main end"<<endl;
    return 0;
}

结果:

 

右值引用引用将亡值后,将亡值的生存期就随从右值引用的标识符的生存期,而右值引用rf为静态右值引用,存储在.data区,.data区的数据是在程序运行结束才进行释放,所以当整个程序结束才会销毁该将亡值。

如果rf为非静态右值引用时,将亡值的生存期就只在func函数中有效,即结果为:

3.4具名右值->泛左值

 

int main()
{
    Int&& rv=Int(30);
    Int&& rf=fun(40);
    int&& a=10;
    a+=1;//ok
    &rv;//ok
    &rf;//ok
    &a;//ok
    Int&& ra=rv;//error
    Int&& rb=rf;//error
    int&& b=a;//error
    //此时,rv、rf和a都可以取地址,被称为泛左值,所以不可以再用右值引用去引用rv/rf和a
    Int& rc=rv;//ok
    Int& rd=rf;//ok
    int& c=a;//ok
    const Int& re=rv;//ok
    const Int& rg=rf;//ok
    const int& d=a;
    return 0;
}

3.5值类别转换

int main()
{
    Int a(10);//左值
    const Int b(20);//常左值
    Int&& rv=Int(30);//右值
    //如何让右值引用引用左值:
    Int&& ra=a;//error
    Int&& rb=static_cast<Int&&>(a);//ok,C++的静态类型转换
    Int&& rc=const_cast<Int&&>(b);//ok,C++的去常性类型转换
    //以上两句均可以使用C语言的强转来进行,但C语言的强转是不安全的,尽量使用C++的四种类型转换。
    return 0;
}

3.6函数返回类型不同

3.6.1以值的形式返回(会构建将亡值)

Int fun(int x)
{
	Int tmp(x);
	return tmp;
}
const Int func(int x)
{
	Int tmp(x);
	return tmp;
}
int main()
{
    Int a=fun(1);//ok,编译器进行优化,直接构建a对象
    Int& b=fun(2);//error,fun(2)是一个右值不能以左值引用引用
    const Int& c=fun(3);//ok,左值常引用,可以引用右值
    Int&& d=fun(4);//ok,右值引用引用右值
    const Int&& e=fun(5);//ok,右值常引用
    
    Int a=func(1);//ok,编译器进行优化,直接构建a对象,且该对象不具有常性
    a.SetValue(100);//ok
	a.PrintValue();//ok
    Int& b=func(2);//error,fun(2)是一个右值不能以左值引用引用
    const Int& c=func(3);//ok,左值常引用,可以引用常性右值
    Int&& d=func(4);//error,右值引用不能引用常性右值
    const Int&& e=func(5);//ok,右值常引用
    //对于常性右值引用,编译器编译时将其看成常性左值引用
    return 0;
}

3.6.2以左值引用的形式返回(及其不安全)

Int& fun(int x)
{
    Int tmp(x);
    return tmp;
}
int main()
{
    Int a=fun(1);//编译正确
    Int& b=fun(2);//编译正确
    const Int& c=fun(3);//编译正确
    Int&& d=fun(4);//error,不能使用左值引用初始化右值引用
    cout<<"main end"<<endl;
    return 0;
}

不安全的原因:不能将函数的局部对象或者局部变量以引用的形式返回。引用本质上是指针,在以引用返回时不会构建将亡值,而是直接将tmp对象的地址返回,返回结束后fun函数的栈空间被系统收回,tmp对象已经消亡,用已经消亡的对象去初始化a对象或者引用已死亡的对象是错误的,此时的引用b和c都是失效引用

3.6.3以右值引用的形式返回(依然不安全)

Int&& fun(int x)
{
    return Int(x);
}
int main()
{
    Int a=fun(1);//ok
    Int& b=fun(2);//error,左值引用不能被右值引用初始化
    const Int& c=fun(3);//ok
    Int&& rf=fun(4);//ok
    a.PrintValue();//ok
    //b.PrintValue();
    c.PrintValue();//ok
    rf.PrintValue();//ok
    return 0;
}

 

 

a对象是以已经死亡的对象构建的,c、rf均引用已死亡的对象

3.6.4安全的以引用返回

当对象的生存期不受函数生存期的影响就可以以引用返回。例如:静态变量

Int& fun(int x)
{
    static Int tmp(x);
    return tmp;
}
void add()
{
    int ar[10]={1,2,3,4,5,6,7,8,9,10};
}
int main()
{
    Int a=fun(1);
    Int& b=fun(2);
    const Int& c=fun(3);
    a.PrintValue();
    b.PrintValue();
    c.PrintValue();
    add();
    b.PrintValue();
    c.PrintValue();
    cout<<"main end"<<endl;
    return 0;
}

 全是1的原因是:静态对象只创建一次当程序结束才消亡。当第一次调用fun(1)函数时,静态对象tmp被创建并初始化为1,在返回时拷贝构造了a对象;当第二次调用fun(2)函数时,静态对象tmp在第一调用中已经创建好了不会再被创建,所以其值依然为1,并且b作为静态对象tmp的引用;同样的,c也作为其引用,所以值均为1。由于静态对象在.data区中,在调用add()函数后不会清扰tmp的数据,所以值依然为1。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/657316.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

网络安全竞赛——综合靶机渗透测试ZHCS-2全过程解析教程

任务一:综合靶机渗透测试 任务环境说明: 服务器场景:ZHCS-2(关闭连接)服务器场景操作系统:版本不详扫描目标靶机将靶机开放的所有端口,当作flag提交(例:21,22,23) FLAG:22,80 扫描目标靶机将靶机的http服务版本信息当作flag提交(例:apache 2.3.4) FLAG: ligh…

Vue3 Hooks函数使用及封装思想

目录 一. 什么是hooks函数&#xff1f; 二、如何封装一个hooks函数 三、Hooks 常用 Demo &#xff08;1&#xff09;验证码倒计时 &#xff08;2&#xff09;防抖 &#xff08;3&#xff09;节流 一. 什么是hooks函数&#xff1f; 专业解释&#xff1a;Vue 3中的Hooks函数…

Nucleo-F411RE (STM32F411)LL库体验 8 - PWM的使用

Nucleo-F411RE &#xff08;STM32F411&#xff09;LL库体验 8 - PWM的使用 1、简述 LD2连接PA5&#xff0c;而PA5可以映射TIM2_CH1&#xff0c;配合TIM2&#xff0c;可以输出PWM。 本片文章大量工作是添加了shell命令&#xff0c;可以通过pwm命令开关pwm以及设置pwm的频率&am…

数字图像处理期末考点整理(全)

计算&#xff1a;傅里叶变换&#xff0c;双线性插值&#xff0c;直方图均衡化&#xff0c;灰度共生矩阵&#xff0c;霍夫曼编码&#xff0c;区域增长/合并&#xff0c;中值滤波 简答&#xff1a;窗口/模板处理&#xff0c;BMP文件存储格式&#xff0c;滤波器和平滑算子的特点&…

Servlet (上篇)

哥几个来学 Servlet 啦 ~~ 目录 &#x1f332;一、什么是 Servlet &#x1f333;二、第一个 Servlet 程序 &#x1f347;1. 创建项目 &#x1f348;2. 引入依赖 &#x1f349;3. 创建目录 &#x1f34a;4. 编写代码 &#x1f34b;5. 打包程序 &#x1f96d;6. 部署程序…

client-go的Indexer三部曲之二:性能测试

欢迎访问我的GitHub 这里分类和汇总了欣宸的全部原创(含配套源码)&#xff1a;https://github.com/zq2599/blog_demos 本篇概览 本文是《client-go的Indexer》系列的第二篇&#xff0c;在前文咱们通过实例掌握了client-go的Indexer的基本功能&#xff0c;本篇咱们尝试对下面这…

css小兔鲜项目搭建

目录 精灵图 精灵图的使用步骤 背景图片大小 background连写 文字阴影 盒子阴影 过渡 骨架标签 SEO三大标签 版心的介绍 css书写顺序 项目结构搭建 精灵图 场景&#xff1a;项目中将多张小图片&#xff0c;合并成一张大图片&#xff0c;这张大图片称之为精灵图 优点…

c语言实现 顺序存储和链式存储(几种链表)

目录 一、简介 二、一些问题 1、递归free 2、free单向循环链表&#xff1a; 3、free单向链表 4、free双向循环链表 5、free使用数组实现链式存储结构 6、sizeof&#xff08;&#xff09;求字符串大小的问题 三、总结 一、简介 花了几天的时间从头开始使用c语言…

UnityVR--UIManager--UI管理2

目录 前言 UIManger的实现 1. 需要用到的变量和数据 2. 在构造中的工作 3. 初始化面板 4. 显示面板 5. 隐藏面板和隐藏所有面板 6. 其他小工具 在场景中实现 1. 不同面板的类型设置 2. 场景中的设置 前言 接前篇&#xff0c;上一篇已经有了UITools.cs其中定义了UI面板需…

Web服务器群集:部署LAMP平台

目录 一、理论 1.LAMP平台 2.Apache网址服务基础 2.httpd服务器的基本配置 3.构建虚拟Web主机 4.MySQL服务 5.构建PHP运行环境 二、实验 1.LAMP架构DISCUZ论坛应用 三、问题 1.虚拟机内存分配上限问题&#xff0c;内存上限只能加到3G。 2.虚拟机CPU如何设置才更加合…

RISC-V 函数调用约定和Stack使用

RISC-V 函数调用约定和Stack使用 引言RISC-V vs x86RISC-V寄存器StackStruct补充函数调用约定寄存器约定函数跳转和返回指令的编程约定被调用函数的编程约定 RISC-V 汇编与 C 混合编程RISC-V 汇编调用 C 函数C 函数中嵌入 RISC-V 汇编 引言 MIT 6.S081 2020 操作系统 本文为M…

1744_Perl获取文件属性参数

全部学习汇总&#xff1a; GreyZhang/perl_basic: some perl basic learning notes. (github.com) 前阵子写通过Perl执行判断调用ImageMagick实现图像的批量压缩功能脚本时用到过这个功能&#xff0c;只是当时仅仅看了一个获取文件大小的功能。 今天看第六版的小骆驼书又看到了…

一篇十分硬核的QT开发经验文章!送给正在做QT开发或想从事QT开发的你

当编译发现大量错误的时候&#xff0c;从第一个看起&#xff0c;一个一个的解决&#xff0c;不要急着去看下一个错误&#xff0c;往往后面的错误都是由于前面的错误引起的&#xff0c;第一个解决后很可能都解决了。 定时器是个好东西&#xff0c;学会好使用它&#xff0c;有时…

别再为缓慢启动而失去用户! 让你的Android应用体验绝佳性能

为什么要启动优化&#xff1f; 启动优化是为了提升应用程序的启动性能&#xff0c;即减少应用程序从启动到可交互状态所需要的时间。以下是一些关键原因&#xff0c;解释了为什么启动优化是重要的&#xff1a; 用户体验&#xff1a; 启动时间是用户与应用程序互动的第一个体验…

常用API

文章目录 1、String类String概述创建对象的两种方式字符串的内容比较String常用APIString类开发验证码功能手机号码屏蔽功能 2、Object类Object类的作用Object类的常用方法**Object的toString方法**Object的equals方法 3、Objects类4、StringBuilder类5、Math类6、System类7、B…

2023-06-17:说一说redis中渐进式rehash?

2023-06-17&#xff1a;说一说redis中渐进式rehash&#xff1f; 答案2023-06-17&#xff1a; 在Redis中&#xff0c;如果哈希表的数组一直保持不变&#xff0c;就会增加哈希冲突的可能性&#xff0c;从而降低检索效率。为了解决这个问题&#xff0c;Redis会对数组进行扩容&am…

基于Spark的气象数据分析

研究背景与方案 1.1.研究背景 在大数据时代背景下&#xff0c;各行业数据的规模大幅度增加&#xff0c;数据类别日益复杂&#xff0c;给数据分析工作带来极大挑战。气象行业和人们的生活息息相关&#xff0c;随着信息时代的发展&#xff0c;大数据技术的出现为气象数据的发展…

第九章 形态学图像处理

文章目录 9形态学图像处理9.2腐蚀与膨胀9.2.1腐蚀9.2.2膨胀 9.3开操作和闭操作9.5一些基本形态学方法9.3.1边界提取 9.6灰度级形态学9.6.3一些基本的形态学算法 9形态学图像处理 9.2腐蚀与膨胀 9.2.1腐蚀 imgcv2.imread(dige.png,0) kernel np.ones((3,3),np.uint8) num[[…

第七章 原理篇:HOG特征提取

之前面试被问到了然后没有讲出来&#xff0c;所以今天复习一下&#xff01; 气死我了&#xff01; 参考教程&#xff1a; What Is a Feature Descriptor in Image Processing? https://medium.com/analytics-vidhya/a-gentle-introduction-into-the-histogram-of-oriented-…

scratch lenet(3): 直方图均衡化的C语言实现

文章目录 1. 目的2. 原理3. 实现3.1 获得直方图 int hist[256]3.2 获得累积分布 int cdf[256]3.3 均衡化公式3.4 遍历原图&#xff0c;逐点均衡化&#xff0c;得到结果 4. 完整代码和结果4.1 例子14.2 例子24.3 例子34.4 完整代码 5. References 1. 目的 用 C 语言实现直方图均…