c++ 左值、右值、左值引用()、右值引用(),移动构造和std::move

左值和右值 不是等于号的左边和右边 !!(一部分场景下是这样)

右值可以描述成一个临时值

c++ 左值、右值、左值引用、右值引用&&

    • 左值
    • 右值
    • 左值引用
    • 右值引用
      • 结论
    • 第二弹~ 你可以完全不看上面的解释
    • 移动语义
        • 移动构造和move

左值

英文简写为“lvalue”,是“locator value”

最初指的是可出现在赋值语句左边的实体(就是等于号左边的值)

本质上是是 可以取到它的地址的值的内存区域(一般可以通过&)

常见的左值有:
1、变量(对象);

int i = 0;			i = 1;				// i是变量(对象),左值

2、const 变量(对象);

const int ci = 5;	//ci = 6;			// ci是const变量(对象),是左值但不能出现在赋值号左边

3、对指针解引用,*(指针);*pi = 1;
4、数组元素,a[1]
5、结构体成员、类成员,s.m_aps->ma

st.m_a = 1;			// 结构体成员,左值pst->m_b = 2;

右值

英文简写为“rvalue”,是"read value"的缩写

指一种表达式,其结果是值而非值所在的位置。一般是没有明确的内存位置,无法使用&获取地址,值不可被修改的。

另外,很多时候左值可以当右值使用。

常见的右值有:
1、字面常量(立即数),引号括起的字符串除外(它们由其地址表示),42、‘a’;
2、算术运算符(+、-、*、/、%、正号、负号)的求值结果,1+2;
3、逻辑运算符(&&、||、!)的求值结果,a!=10;
4、关系运算符(>、>=、<、<=、==、!=)的求值结果,a>10 && a<100;
5、函数的非引用返回值。这种返回值位于临时内存单元中,运行到下一条语句时,它们可能不再存在;

	i = get100();		// 函数的非引用返回值,右值

6、当条件运算符的两个表达式都是左值或者能转换成同一种左值类型时,运算的结果是左值;否则运算的结果是右值。

左值引用

为了与右值引用区分开,把C++11之前出现的引用成为左值引用。

左值引用就是C++中的引用类型,是己定义的变量的别名,主要用作函数形参或返回值。

// g++ 13_lvalue_reference.cpp
int get100()
{return 100;
}
int main()
{int i = 0;int &ri = i;		// 左值引用
//	int &*pri = &ri;	// 不能定义引用的指针。报错:cannot declare pointer to ‘int&’
//	int &&rri = ri;		// 不能定义引用的引用。在C++11标准里,这是一个右值引用int *pi = &i;int *&rpi = pi;		// pi是左值,rpi是指针的引用int &r_pi = *pi;	// *pi是左值,可以初始化左值引用int arr[5] = {0};int &rarr = arr[0];	// arr[0]是左值,可以初始化左值引用// const 左值引用可以被初始化为一些左值const int &cri = i;		// const 左值引用
//	const int *&crpi = pi;	// 报错:用 ‘int*’ 初始化 ‘const int*&’ 无效const int &cr_pi = *pi;	// const 左值引用const int &crarr= arr[0];// const 左值引用可以被初始化为右值   去掉const不行const int &ri0 = 42;const int &ri1 = 'a';const int &ri2 = 1+2;const int &ri3 = (ri1 != 'a');const int &ri4 = (ri1 >= 'a');const int &ri5 = get100();		// 函数的非引用返回值,右值const int &ri6 = i>0?1:0;	
//	ri0 = 1;	// 报错,const引用是只读的,其值不能修改return 0;
}

右值引用

右值引用:这是为了支持移动操作,C++11标准引入的一种新的引用类型

所谓右值引用就是必须绑定到右值的引用。

通过 && 而不是 & 来获得右值引用。

怎样定义右值引用?右值引用使用&&来定义,也是必须初始化,初始化后无法改变其绑定的对象。定义右值引用格式:类型 &&引用名称 = 右值;。定义一个右值引用可以参考下面代码:

int &&rl = 13;
int *pi = &rl;

将右值关联到右值引用导致该右值被存储到特定的位置,且可以获取该位置的地址。也就是说,虽然不能将运算符&用于 13,但可将其用于 rl。通过将数据与特定的地址关联,使得可以通过右值引用来访问该数据。

右值引用只能绑定到一个右值,右值要么是字面常量,要么是在表达式求值过程中创建的临时对象;

而临时对象有两个特点:
1、该对象将要被销毁;
2、该对象没有其他用户再使用它。
这就意味着,右值引用的代码是最后使用这个对象的了,可以自由地接管所引用的对象的资源。

在这里插入图片描述

如上图,变量a、b相加之后会产生一个值,这个值就是临时量,但它在内存中肯定是存在某个地址的,没有右值引用之前,这个值使用完就会被销毁,我们也不会知道它的内存地址。现在,这块内存可以被右值引用关联,关联后,右值引用甚至可以改变内存的内容,等右值引用使用完再销毁。

// g++ 13_rvalue_reference.cpp  -std=c++11
#include <iostream>
using namespace std;
int get100()
{return 100;
}void fun(int &&rri)// 右值引用作为函数形参
{rri = 0;
}
int main()
{// 1、右值引用必须被初始化为右值int &&ri0 = 42;		// 将 42 存到一个临时量,然后引用这个临时量int &&ri1 = 'a';	// 将 'a' 存到一个临时量,然后引用这个临时量int &&ri2 = 1+2;	// 将 1+2 存到一个临时量,然后引用这个临时量int &&ri3 = (ri1 != 'a');int &&ri4 = (ri1 >= 'a');int &&ri5 = get100();	// 函数的非引用返回值,右值int &&ri6 = ri0>0?1:0;// 2、右值引用的内容可以被修改ri0 = 1;cout << "ri0=" << ri0 << endl;// 3、虽然没办法获取右值的地址,但可以获取右值引用的地址,并改变该地址的值int *pi = &ri0;*pi = 2;cout << "pi=" << pi << ", *pi=" << *pi << ", ri0=" << ri0 << endl;// 4、传入右值fun(1+2);return 0;
}

gpt补充:

  1. ri0 = 1;

    :

    • 这是直接使用右值引用 ri0 进行赋值操作。尽管 ri0 引用了一个右值临时变量,但 ri0 本身是一个左值,可以直接给它赋值。
    • 此操作将临时变量的值直接修改为 1,且没有解引用操作,简单直接。
  2. *pi = 1;

    :

    • 这里 pi 是一个指向 ri0 的指针,*pi = 1; 解引用指针来访问临时变量的值。
    • 从结果上看,*pi = 1;ri0 = 1; 会导致相同的效果,即右值临时变量的值被修改成 1
    • 但使用指针间接访问对象相对稍微多了一层解引用操作。

结论

虽然这两种操作可以达到相同的效果,但直接使用 ri0 赋值(ri0 = 1;)在语法上更直接,性能上可能稍微优于通过指针赋值。

第二弹~ 你可以完全不看上面的解释

#include <iostream>
#include <string>void printA(const int& s)
{//const的左值引用可以接受 左值和右值std::cout << s << std::endl;
}
void printB(int&& s)
{//右值引用只能接受右值std::cout << "右值" ;std::cout << s << std::endl;}
//如果printB 名字改成printA 也就是重载,代码printB(1); 也会先执行printA(int&& s) 优先级
int main(int argc, char const *argv[])
{// demo1 说明左值引用int i = 1;int &ii = i;ii = 2;                       // 这是修改 不是初始化赋值std::cout << i << std::endl;  // 2std::cout << ii << std::endl; // 2// 补充: 左值引用指向的地址不会变化,但是值会变化!// demo2 说明左值引用是不能用右值的 除非.....//  int &a=1; 报错,右值是不能初始化赋值給左值引用的//  int const &a=1;   //   编译报错'const int& a' previously declared hereconst int &b = 1; // 除非是常量引用() const// b=i;  b=2  常量不可修改std::cout << b << std::endl; // 1//demo3 // 左值引用在调用方法时的使用printA(i);printA(1); //demo4  右值引用在调用方法时的使用//printB(i);//报错  printB(1); return 0;
}

移动语义

本质上允许我们移动对象,而不是复制对象到其他地方


为什么有移动语义

class Person {
public:string name;int age;Person(const Person& other) { // 拷贝构造函数name = other.name;age = other.age;}
};int main() {Person p1("Alice", 30); // 创建 Person 对象Person p3=p1;//!!!这样也会调用拷贝构造函数,这是全新的对象 cout << p2.name << ", " << p2.age << endl; // 输出:Alice, 30return 0;
}

那么为什么不能直接把p1给p3 假如p1是一个临时量 ,或者是当它是一个右值的时候

移动构造和move
// g++ 14_Copy_Date.cpp -std=c++11
#include <iostream>
#include <stdio.h>
#include <string.h>using namespace std;#define MAX_NEW_MEM (64 * 1000 * 1000)class CDate
{
public:CDate(int year, int mon, int day){m_year = year;m_mon = mon;m_day = day;str = new char[MAX_NEW_MEM];sprintf(str, "%4d.%02d.%02d", year, mon, day);cout << "Calling Constructor" << ", this=" << this << endl;}// 拷贝构造函数定义CDate(const CDate &date){m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = new char[MAX_NEW_MEM];memcpy(str, date.str, MAX_NEW_MEM);cout << "Calling Copy Constructor" << ", this=" << this << ", Copy Data" << endl;}//移动构造CDate(CDate&& date) noexcept //加上noexcept,用于通知标准库不抛出异常。提高性能{m_year = date.m_year;m_mon = date.m_mon;m_day = date.m_day;str = date.str;//直接指向了 省去了copydate.str = NULL;//把原来指针的指向null 防止悬挂指针cout << "Calling Move Constructor" << ", this=" << this <<endl;}// 析构函数定义~CDate(){cout << "Calling Destructor" << ", this=" << this << endl;delete[] str;}CDate operator+(int day){CDate temp = *this;temp.m_day += day;cout << "Calling operator+" << ", this=" << &temp << endl;return temp;}void show(){cout << "Date: " << m_year << "." << m_mon << "." << m_day << ", this=" << this << endl;// cout << "Date: " << str << endl;}private:int m_year;int m_mon;int m_day;char *str;
};int main()
{CDate date(2024, 06, 07);cout << endl;CDate date1 = date;cout << endl;/* Calling Constructor, this=0x5ffe70Calling Copy Constructor, this=0x5ffe50, Copy DataCalling Destructor, this=0x5ffe50Calling Destructor, this=0x5ffe70*/return 0;
}int main2()
{CDate date(2024, 06, 07);cout << endl;CDate date2 = move(date);cout << endl;/* Calling Constructor, this=0x5ffe70Calling Move Constructor, this=0x5ffe50Calling Destructor, this=0x5ffe50Calling Destructor, this=0x5ffe70*/return 0;
}

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

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

相关文章

黑马嵌入式开发入门模电基础学习笔记

学习视频: 黑马程序员嵌入式开发入门模电&#xff08;模拟电路&#xff09;基础 文章目录 背景介绍电流电压组件仿真三极管ne555PCBEDA案例&#xff1a;非接触式电笔案例&#xff1a;电子琴 背景介绍 电流 电压 组件 仿真 三极管 mos管 ne555 PCB EDA 案例&#xff1a;非接触…

Tomcat启动过程中cmd窗口(控制台)中文乱码的问题

目录 一、问题产生 二、问题分析 三、解决方法(2种) 一、问题产生 在服务器上使用新的Tomcat9(绿色版ZIP),打开一个cmd窗口后,将路径定位到“tomcat\bin\”目录,运行“startup.bat”。程序会自动打开一个新窗口,这个是Java程序的运行窗口,但是里面的中文全是乱码,如…

Neo4j Desktop 和 Neo4j Community Edition 区别

Neo4j Desktop 和 Neo4j Community Edition 的主要区别在于它们的用途、功能以及安装和管理方式。以下是这两者的详细对比&#xff1a; 1. Neo4j Desktop Neo4j Desktop 是一个图形化的桌面应用程序&#xff0c;主要为开发人员和个人使用提供了一个便捷的环境来安装、管理和运…

FebHost:企业注册.UK域名步骤--了解英国商业环境

企业注册.UK域名步骤&#xff1a;了解英国商业环境 对于希望拓展国际业务的公司和企业家来说&#xff0c;在英国开展业务具有众多优势。英国是一个对企业友好的目的地&#xff0c;吸引着初创企业和国际公司&#xff0c;并将自己定位为首屈一指的全球经济强国&#xff0c;在欧洲…

无人机动力系统测试-实测数据与CFD模拟仿真数据关联对比分析

我们经常被问到这样的问题&#xff1a;“我们计划运行 CFD 仿真&#xff0c;我们还需要对电机和螺旋桨进行实验测试吗&#xff1f;我们可能有偏见&#xff0c;但我们的答案始终是肯定的&#xff0c;而且有充分的理由。我们自己执行了大量的 CFD 仿真&#xff0c;但我们承认&…

cantos7.9系统-部署mysql-8.0.35

前言:MySQL是一个流行的开源关系型数据库管理系统&#xff08;RDBMS&#xff09;&#xff0c;它基于SQL&#xff08;Structured Query Language&#xff09;进行操作。以下是MySQL的一些基本介绍&#xff1a; 开源&#xff1a;MySQL由瑞典MySQL AB公司开发&#xff0c;后来被Su…

预测AI如何提升销售绩效管理:五大方式

每周跟踪AI热点新闻动向和震撼发展 想要探索生成式人工智能的前沿进展吗&#xff1f;订阅我们的简报&#xff0c;深入解析最新的技术突破、实际应用案例和未来的趋势。与全球数同行一同&#xff0c;从行业内部的深度分析和实用指南中受益。不要错过这个机会&#xff0c;成为AI领…

# 第20章 Cortex-M4-触摸屏

第20章 Cortex-M4-触摸屏 20.1 触摸屏概述 20.1.1 常见的触摸屏分类 电阻式触摸屏、电容式触摸屏、红外式触摸屏、表面声波触摸屏 市场上用的最多的是电阻式触摸屏与电容式触摸屏。红外管式触摸屏多用于投影仪配套设备。 电阻式触摸屏构成&#xff1a;整个屏由均匀电阻构成…

Selenium自动化测试

片头 嗨~小伙伴们&#xff0c;今天&#xff0c;我们来开启新的篇章---Selenium自动化测试&#xff0c;准备好了吗&#xff1f;咱们开始咯&#xff01; 一、自动化测试 指通过专门的软件工具和脚本来执行测试任务&#xff0c;而不需要人工干预。它可以自动执行各种测试任务&am…

下一代以区域为导向的电子/电气架构

我是穿拖鞋的汉子&#xff0c;魔都中坚持长期主义的汽车电子工程师。 老规矩&#xff0c;分享一段喜欢的文字&#xff0c;避免自己成为高知识低文化的工程师&#xff1a; 所有人的看法和评价都是暂时的&#xff0c;只有自己的经历是伴随一生的&#xff0c;几乎所有的担忧和畏惧…

RH850-F1KMS1 DMA数据转移

DMA简介 随着汽车电子系统和工业自动化的需求不断增长&#xff0c;DMA&#xff08;Direct Memory Access&#xff0c;直接内存访问&#xff09;技术在提高数据传输效率方面扮演着重要角色。在本篇文章中&#xff0c;我们将探讨RH850微控制器如何高效实现DMA传输&#xff0c;以…

MOSFET电路栅源极GS之间并联电容后,MOS炸管原因分析

1、前言 在介绍&#xff0c;在进行MOSFET相关的电路设计时&#xff0c;可能会遇到MOSFET误导通的问题&#xff0c;为了解决此问题&#xff0c;我们提出了两种方法&#xff0c;一种是增大MOSFET栅极串联电阻的阻值&#xff0c;另外一种是在MOSFET栅-源极之间并联一个电容&#…

Keil uvision的edition

0 Preface/Foreword 0.1 参考网址 https://zhuanlan.zhihu.com/p/456069876 1 Keil版本介绍 版本介绍&#xff1a; Keil Lite&#xff08;免费版&#xff09;&#xff1a;最多32KB代码&#xff0c;无法使用中间件Keil Essential&#xff08;基础版&#xff09;&#xff1a;没…

I/O文件:文件的关闭

int fclose(FILE *stream); 成功关闭返回1&#xff0c;关闭失败返回EOF即-1&#xff0c;并设置errno。 流关闭时自动刷新缓冲中的数据并释放缓冲区 当一个程序正常终止时&#xff0c;所有打开的流都会被关闭 流一旦关闭就不能执行任何操作。 运行结果&#xff1a; 若未成功打…

联邦学习的未来:深入剖析FedAvg算法与数据不均衡的解决之道

引言 随着数据隐私和数据安全法规的不断加强&#xff0c;传统的集中式机器学习方法受到越来越多的限制。为了在分布式数据场景中高效训练模型&#xff0c;同时保护用户数据隐私&#xff0c;联邦学习&#xff08;Federated Learning, FL&#xff09;应运而生。它允许多个参与方…

深入理解Flutter生命周期函数之StatefulWidget(一)

目录 前言 1.为什么需要生命周期函数 2.开发过程中常用的生命周期函数 1.initState() 2.didChangeDependencies() 3.build() 4.didUpdateWidget() 5.setState() 6.deactivate() 7.dispose() 3.Flutter生命周期总结 1.调用顺序 2.函数调用时机以及主要作用 4.生…

LoFTR: Detector-Free Local Feature Matching with Transformers

LoFTR: Detector-Free Local Feature Matching with Transformers 整体概括 Loftr特征点匹配算法与传统的特征点匹配算法的优势&#xff1a; 不需要先得到特征点&#xff0c;这也就解决了第一个问题End2End的方式&#xff0c;用起来比较方便&#xff0c;效果也更好 整体流程的…

免押租赁系统的优势与应用前景分析

内容概要 免押租赁系统是一种新兴的租赁形式&#xff0c;它利用了信用大数据与区块链技术的优势&#xff0c;帮助用户摆脱了传统租赁中常见的押金烦恼。通过这种方式&#xff0c;用户不仅可以体验到更低的租用门槛&#xff0c;还能享受到更顺畅的交易过程。用户只需提供基本的…

「Qt Widget中文示例指南」如何创建一个窗口标志?(二)

Qt 是目前最先进、最完整的跨平台C开发工具。它不仅完全实现了一次编写&#xff0c;所有平台无差别运行&#xff0c;更提供了几乎所有开发过程中需要用到的工具。如今&#xff0c;Qt已被运用于超过70个行业、数千家企业&#xff0c;支持数百万设备及应用。 窗口标志要么是类型…

(附项目源码)Java开发语言,211 springboot 在线问诊系统的设计与实现,计算机毕设程序开发+文案(LW+PPT)

摘 要 针对医院门诊等问题&#xff0c;对在线问诊进行研究分析&#xff0c;然后开发设计出在线问诊系统以解决问题。在线问诊系统主要功能模块包括首页、轮播图管理、公告信息管理、资源管理、系统用户管理&#xff08;管理员、患者用户、医生用户&#xff09;、模块管理&#…