C++11右值引用理解

C++11之前只有引用这个概念,不存在什么左值引用右值引用。C++11后更新了众多新特性,其中右值引用较为重要,这里对右值引用做一个学习记录。

1 左值与右值

1.1左值

左值比较好理解,能够取到地址的变量、对象等都是左值。

1.2 右值

右值即不能取到地址的对象,此类对象大都为临时变量,比如=右边的值,表达式,函数返回值

int x = 10,y = 10;      //以此为代表的大部分‘=’右边的对象,也称作字面量
&10;                     //报错
string s = "hello world";//右值不包括字符串,对字符串取地址是可以取到值的
&“hello world”;          //不报错x+y;//x+y也是右值
&(x+y);//报错int getSum(int x,int y){ int sum = x+y;return sum;}//注意,这里指的函数返回值不是sum,而是函数返回给调用他的上级函数时的值(临时变量)
&getSum(x,y);//报错,大部分函数返回值都不能取地址,但也有部分能取地址,如返回引用的函数

2、左值引用,右值引用

对左值的引用即作指引用;对右值的引用即右值引用。

int a=0;
int &left_ref=a;//左值引用
int &&right_ref=0;//右值引用
int&& sum = getSum(x,y);//sum是右值引用

3、问题铺垫,便于理解

1、引用占用内存空间吗?

从定义的角度来看,引用是变量的别名,不占用内存空间;但从引用的实现来看,引用是通过指针实现,指针是占用内存空间的,所以引用必定占用内存空间。
2、左右值引用能被哪些值赋值

/*左值引用可以被如下3种值赋值,也就是说左值引用做形参可以传下列值*/
int a = 1;
int &left_ref = a;//左值
int &left_ref2 = left_ref ;//左值引用int &&right_ref = 10;
const int &left_ref3 = right_ref;//右值引用/*右值引用可以被如下3种方式赋值:1右值,2右值,3右值。
没错,只能被右值赋值,右值引用也不能赋值右值引用*/
int &&right_ref = 10;//右值
int &&right_ref2 = right_ref;//报错:无法将右值引用绑定到左值。//从以上报错来看,右值引用其实是一种左值

3、函数返回过程

int getSum(int x,int y){return x+y;
}
在以上函数中,函数进行返回时,将x+y的值进行拷贝,构造出一个临时变量,
该函数也以这个临时变量返回给上一级调用者,返回完成后(上级函数调用该函数代码行执行完),这个临时变量被销毁。
如果返回值是类的对象,则会调用构造函数进行拷贝,析构函数进行销毁。

4、左值和右值能否互相转换

//左值转右值
int i = 0;
int &&k = static_cast<int&&>(i); 
int &&m = std::move(i);//本质还是上面的强转,但更建议使用这个//右值不能转换为左值,但右值引用就已经是左值了
int move(int &&val){//这里形参是右值引用,但传入函数后,相对这个函数来说它是左值&val;//不报错
}

4、右值引用的作用

右值引用新特性的出现肯定是为开发者提供更多的便利,提高C++效率,主要作用体现在以下两处。

4.1 移动语义

移动语义其实就是为了减少重复的内存拷贝,而使用移动的方式实现资源转移。这种方式被称为移动拷贝,在开发中具体体现为移动拷贝函数或者移动运算符重载函数。

假如有以下函数调用过程(编译需要添加-fno-elide-constructors选项,否则临时变量构建过程看不到,即关闭RVO函数返回值优化):

#include <iostream>
#include <cstring>class BigMemoryPool {
public:static const int PoolSize = 4096;BigMemoryPool() : pool_(new char[PoolSize]) {}~BigMemoryPool(){if (pool_ != nullptr) {delete[] pool_;}}BigMemoryPool(const BigMemoryPool& other) : pool_(new char[PoolSize]){std::cout << "copy big memory pool." << std::endl;memcpy(pool_, other.pool_, PoolSize);}
private:char *pool_;
};BigMemoryPool get_pool(const BigMemoryPool& pool)
{return pool;
}BigMemoryPool make_pool()
{BigMemoryPool pool;return get_pool(pool);
}int main()
{BigMemoryPool my_pool = make_pool();
}输出结果:
copy big memory pool.
copy big memory pool.
copy big memory pool.

这里可以看到,执行了3次拷贝构造函数,但其实都是中间量,生命周期极短,并没有什么实际意义,如果能够进行移动的话至少可以减少几次拷贝,所以我们添加一个移动构造函数:

#include <iostream>
#include <cstring>class BigMemoryPool {
public:static const int PoolSize = 4096;BigMemoryPool() : pool_(new char[PoolSize]) {}~BigMemoryPool(){if (pool_ != nullptr) {delete[] pool_;}}BigMemoryPool(BigMemoryPool&& other){std::cout << "move big memory pool." << std::endl;pool_ = other.pool_;other.pool_ = nullptr;}BigMemoryPool(const BigMemoryPool& other) : pool_(new char[PoolSize]){std::cout << "copy big memory pool." << std::endl;memcpy(pool_, other.pool_, PoolSize);}
private:char *pool_;
};BigMemoryPool get_pool(const BigMemoryPool& pool)
{return pool;//调用一次复制构造,这里是const左值,所以调用拷贝构造
}BigMemoryPool make_pool()
{BigMemoryPool pool;return get_pool(pool);//调用一次移动构造,因为返回的是右值
}int main(){BigMemoryPool my_pool = make_pool();//调用一次移动构造,因为返回的是右值
}输出结果:
copy big memory pool.
move big memory pool.
move big memory pool.

可以看到减少了两次拷贝构造。

4.2 完美转发

这是一个常规的函数转发模板:

#include <iostream>
#include <string>
template<class T>
void show_type(T t)
{std::cout << typeid(t).name() << std::endl;
}
template<class T>
void normal_forwarding(T t)
{show_type(t);
}
int main()
{std::string s = "hello world";normal_forwarding(s);
}

其中转发函数按照值传递的方式进行转发,std::string在转发过程中会额外发生一次临时对象的复制。进行以下修改:

#include <iostream>
#include <string>
template<class T>
void show_type(T t)
{std::cout << typeid(t).name() << std::endl;
}
template<class T>
void perfect_forwarding(T &&t)
{show_type(static_cast<T&&>(t));
}
std::string get_string()
{return "hi world";
}
int main()
{std::string s = "hello world";perfect_forwarding(s);perfect_forwarding(get_string());
}

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

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

相关文章

MySQL基础篇-约束

目录 1.约束概述 2.分类 3.测试user表的约束情况 主键约束 非空约束及唯一约束 检查约束 默认约束 4.外键约束 外键约束的语法 外键约束的删除/更新行为 小结 1.约束概述 MySQL约束&#xff08;Constraints&#xff09;是用于确保表中数据完整性和一致性的规则。它们定…

多线程(虚拟地址空间)

代码展示线程 既然我们提到了&#xff0c;线程隶属于进程&#xff0c;是进程的一个执行分支 真的是这样吗&#xff1f; 我们还需要用代码来验证 初步思路是创建三个线程&#xff0c;其中main函数里面的为主线程 不断循环&#xff0c;并且打印相应的pid 假如它们属于不同的进程…

四,立方体贴图

Pbr的间接光用到立方体贴图&#xff0c;所以&#xff0c;先用shader进行立方体贴图。 立方体贴图很简单&#xff0c;就是用方向向量&#xff08;不一定是单位向量&#xff09;采样cubeMap的颜色。 也就是在片元着色器中传递。 "float x outPos.r;\n" "float y…

位运算符与高级操作

位运算符与高级操作 运算符 高级操作 左移实现乘法 左移n位等价于乘以2的n次方 int x; x 2; x x << 2; x x << 3;使用左移实现乘法运算仅限于乘以2的倍数 是不是只要左移就能够实现乘以2的倍数呢? char x 120; x x << 1;右移实现除法 右移n位等价于除…

查看基站后台信息

查看基站后台信息 电脑配置固定ip: 192.168.1.99: 打开“网络和共享中心”&#xff0c;选择更改适配器设置&#xff1a; 右键“本地连接”&#xff0c;选择属性 基站网线直连电脑网口 Telnet 登录基站 打开dos窗口 windows键R”&#xff0c;输入cmd&#xff0c;点确定&…

MySQL的执行流程

在聊mysql的执行流程之前&#xff0c;咱们要先聊聊mysql的逻辑架构。 逻辑架构 可以将上图简化为下图 连接层 客服端访问mysql服务器前&#xff0c;要先和mysq建立tcp连接。经过3次握手建立连接成功后&#xff0c;mysql服务器对tcp传输过来的账号密码进行身份认证&#x…

【大数据】Doris 构建实时数仓落地方案详解(二):Doris 核心功能解读

本系列包含&#xff1a; Doris 构建实时数仓落地方案详解&#xff08;一&#xff09;&#xff1a;实时数据仓库概述Doris 构建实时数仓落地方案详解&#xff08;二&#xff09;&#xff1a;Doris 核心功能解读Doris 构建实时数仓落地方案详解&#xff08;三&#xff09;&#…

Selenium —— Web自动化多浏览器处理!

一、多浏览器测试介绍 1.1、多浏览器测试背景 用户使用的浏览器(firefox,chrome,IE 等)web 应用应该能在任何浏览器上正常的工作&#xff0c;这样能吸引更多的用户来使用 1.2、多浏览器测试概述 是跨不同浏览器组合验证网站或 web 应用程序功能的过程是兼容性测试的一个分支…

git学习使用

git使用 1、cmd #查看版本 git version2、初识 Git GUI: Git提供的图形界面工具 Git Bash: Git提供的命令行工具 1.打开Git Bash2.设置自己的用户名和邮箱地址git config --global user.name "xxx"git config --global user.email "123456789163.com"查…

大数据Flink(八十七):DML:Joins之Regular Join

文章目录 DML:Joins之Regular Join DML:Joins之Regular Join Flink 也支持了非常多的数据 Join 方式,主要包括以下三种: 动态表(流)与动态表(流)的 Join动态表(流)与外部维表(比如 Redis)的 Join动态表字段的列转行(一种特殊的 Join)细分 Flink SQL 支持的

【数据结构与算法】链表的实现以及相关算法

目录 单选链表的基本实现 有序列表的合并&#xff08;双指针法&#xff09; 链表的反转 链表实现两数之和 判定链表是否有环 双链表的实现 public class DLinkedList<E> {private Node<E> first;private Node<E> last;int size;/*** 头插法* param i…

Prettier - Code formatter格式化规则文件

文章目录 前言安装使用 前言 先前公司在规范代码时,由于个人业务繁忙跟技术总监是后端出身用的IDEA不熟悉vsCode;以及大多数时都自己一个人负责一个项目,当时并不看重这些;最近在整理vue3tsvite的脚手架模板(平时工作用的react),开始整理格式化代码,方便之后 vue 和 react 中应…

Android Shape设置背景

设置背景时&#xff0c;经常这样 android:background“drawable/xxx” 。如果是纯色图片&#xff0c;可以考虑用 shape 替代。 shape 相比图片&#xff0c;减少资源占用&#xff0c;缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…

高效查询大量快递信息,轻松掌握技巧

在如今快节奏的生活中&#xff0c;快递已经成为我们日常不可或缺的一部分。然而&#xff0c;对于一些忙碌的人来说&#xff0c;单个查询每一个快递单号可能会浪费太多时间。因此&#xff0c;我们需要一款可以帮助我们批量查询快递的软件。 在市场上&#xff0c;有很多款专门用于…

【2023年11月第四版教材】第15章《风险管理》(第四部分)

第15章《风险管理》&#xff08;第四部分&#xff09; 8 过程4-实施定量风险分析8.1 实施定量风险分析★★★8.2 数据分析★★★8.3 定量成本风险分析S曲线示例8.4 决策树示例8.5 龙卷风图示例8.6 项目文件&#xff08;更新&#xff09;★★★ 9 过程5-规划风险应对9.1 规划风险…

【2023款奔驰改款E260 L运动型:豪华与性能的完美结合】

在汽车市场中&#xff0c;奔驰一直以其卓越的品质和卓越的性能赢得了消费者的喜爱。而2023款奔驰改款E260 L运动型&#xff0c;更是将豪华与性能完美结合&#xff0c;让人无法抗拒。首先&#xff0c;让我们来看一下这款车的外观设计。新款E260 L运动型的前脸设计更加犀利&#…

【Linux】——基操指令(一)

个人主页 代码仓库 C语言专栏 初阶数据结构专栏 Linux专栏 LeetCode刷题 算法专栏 目录 前言 基操前的碎碎念 计算机的层状结构 基础指令 查看登录用户指令 查看用户指令 查看当前所处工作目录 清屏指令 基操指令 ls命令 cd命令 makdir指令 rmdir指令 &…

十二、MySql的事务(下)

文章目录 一、事务隔离级别二、如何理解隔离性三、隔离级别&#xff08;一&#xff09;读未提交【Read Uncommitted】&#xff1a;&#xff08;二&#xff09;读提交【Read Committed】 &#xff1a;&#xff08;三&#xff09;可重复读【Repeatable Read】&#xff1a;&#x…

【计算机网络笔记六】应用层(三)HTTP 的 Cookie、缓存控制、代理服务、短连接和长连接

HTTP 的 Cookie HTTP 的 Cookie 机制要用到两个字段&#xff1a;响应头字段 Set-Cookie 和请求头字段 Cookie。 Cookie 可以设置多个 key-value 对&#xff0c; 响应头中可以设置多个 Set-Cookie 字段&#xff0c;请求头Cookie后面可以设置多个键值对&#xff0c;用分号隔开&a…

西门子KTP触摸屏做画面时如何把设备图片或Logo做到画面上?

西门子KTP触摸屏做画面时如何把设备图片或Logo做到画面上&#xff1f; 如下图所示&#xff0c;新建一个项目&#xff0c;添加一个触摸屏设备&#xff0c;这里以TP1200 Comfort触摸屏为例进行说明&#xff0c;双击进入根画面&#xff0c; 如下图所示&#xff0c;在右侧的工具箱中…