C++右值引用与move和forward函数的使用详解
作者:Maxwell.. 发布时间:2023-07-05 19:27:33
1、右值
1.1 简介
首先区分一下左右值:
左值是指存储在内存中、有明确存储地址(可取地址)的数据;
右值是指可以提供数据值的数据(不可取地址)
如int a=123;123是右值, a是左值。总的来说 可以对表达式取地址(&)就是左值,否则为右值
而C++11 中右值又可以分为两种:
纯右值:非引用返回的临时变量、运算表达式产生的临时变量如a+b、原始字面量和 lambda 表达式等
将亡值:与右值引用相关的表达式、返回T&& 类型函数的返回值
1.2 右值引用
常见的 & 为左值引用、右值引用使用 && 表示
int&& a = 123;
int &b = a;
int &&c = a;//不合法
如上 a 是对123的右值引用,但是a本身是左值,其在内存中有明确的存储地址,所以c不能再对其进行左值引用。
1.3 右值引用的意义
可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高应用程序的性能。
#include <iostream>
using namespace std;
class Test
{
public:
Test() : num(new int(100))
{
cout << "construct" << endl;
}
Test(const Test& a) : num(new int(*a.num))
{
cout << "copy construct" << endl;
}
// 移动构造函数其实就是将入参的资源赋值给自己,并将入参的对应资源指针制空,
Test(Test&& a) : num(a.num)
{
cout << "rv copy construct" << endl;
a.num = nullptr;
}
~Test()
{
delete num;
}
int* num;
};
Test getObj()
{
Test t;
return t;
}
int main()
{
Test t = getObj();
return 0;
};
getObj()会得到一个非引用的临时对象,是纯右值,如果只有拷贝构造函数就只能再次new一块区域去保存该右值的资源,这是因为不能确定拷贝构造传入的参数后面是不是还会继续被使用, 只好进行深拷贝。而对于传入右值的情况,可以确定右值以后不会再进行访问,因此可直接将其指针复给新对象,将入参的对应指针置为null,防止析构造成野指针,避免深拷贝带来的性能消耗。
由此可见,右值引用具有移动语义:将确定后续不再使用的对象中的资源转移给新的对象,虽然左值引用也能够做到资源转移,但传入的左值后续可能还会被更改和使用,个人认为右值引用恰好做到了这种区分。
2、move
使用std::move方法可以将左值转换为右值。使用这个函数并不能移动任何东西,而是和移动构造函数一样都具有移动语义,将对象的状态或者所有权从一个对象转移到另一个对象,只是转移,没有内存拷贝。
当确定一个变量a后续不会再进行使用,并且需要将其赋值给另一个对象时,可以使用移动构造来转移资源
Test a;
Test && b = a; // error
上面的操作是不可行的,因为a不是一个右值,要想调用Test的移动构造函数,就必须将a这个左值转变为一个右值:使用move() 函数
Test a;
Test && b = move(a); // ok
std::move 基本等同于一个类型转换:
static_cast<T&&>(lvalue)
3、foward
move将左值转换为右值,foward可以满足更多的情形
std::forward<T>(t);
当T为左值引用类型时,t将被转换为T类型的左值
当T不是左值引用类型时,t将被转换为T类型的右值
int a = 123;
foward<int&>(a); // a转换为左值并返回
foward<int&&>(a); // a转换为右值并返回
foward<int>(a); // a转换为右值并返回
来源:https://blog.csdn.net/ulan420/article/details/126363446
猜你喜欢
- 简介对于一个APP来说,肯定会有一个AppBar,这个AppBar一般包含了APP的导航信息等。虽然我们可以用一个固定的组件来做为AppBa
- 协议做如下规定:规定数据协议:序列号 长度 状态字 数据长度 数据1 &n
- 前言本篇文章 中写到的是 flutter 调用了Android 原生的 TextView 案例添加原生组件的流程基本上可以描述为:1 and
- 相比于直线检测,直线拟合的最大特点是将所有数据只拟合出一条直线void fitLine( InputArray points, Output
- 报错翻译: compileSdkVersion android-24”需要JDK 1.8或更高版本编译。报错现象如下图:原因:st
- 先上图下拉刷新跟原生开发一样,下拉刷新在flutter里提供的有组件实现 RefreshIndicator一直不明白为啥组件中都提供下拉刷新
- 前言当系统的并发比较高的时候,日志的处理输出也是一种性能的开销负担,所以,选择一个中间件来处理消费日志必不可少!下面是spring boot
- forward_list 概述forward_list 是 C++ 11 新增的容器,它的实现为单链表。forward_list 是支持从容
- [LeetCode] 2. Add Two Numbers 两个数字相加You are given two non-empty&n
- 最近在做上传文件的服务,简单看了网上的教程。结合实践共享出代码。由于网上的大多数没有服务端的代码,这可不行呀,没服务端怎么调试呢。Ok,先上
- 项目概况:Spring Cloud搭的微服务,使用了eureka,FeignClient,现在遇到FeignClient调用接口时不支持上传
- 目标效果: 点击动画按钮之后每张牌各自旋转 散开到屏幕上半部分的任意位置之后回到初始位置 比较像LOL男刀的技能动画 : )1: 创建卡牌对
- 在使用springMVC框架构建web应用,客户端常会请求字符串、整型、json等格式的数据,通常使用@ResponseBody注解使 co
- 您已经看到很多包含视频内容的应用程序,比如带有视频教程的食谱应用程序、电影应用程序和体育相关的应用程序。您是否想知道如何将视频内容添加到您的
- 这篇文章主要介绍了Java如何实现自定义异常类,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参
- 要实现摇一摇的功能,类似于微信的摇一摇方法1:通过分析加速计数据来判断是否进行了摇一摇操作(比较复杂)方法2:iOS自带的Shake监控AP
- 这节主要完成一些基本的增删改查以及Service、Dao和Action的抽取。1. Service层的抽取  
- 废话开篇:iOS与android在实现列表界面的时候是有重用机制的,目的就是减少内存开销,用时间换空间。个人感觉flutter并没有特别强调
- jdk8之前 一、java.lang.Systemlong times = System.currentTimeMillis();
- BitArray的基础可以看菜鸟编程BitArray 类管理一个紧凑型的位值数组,它使用布尔值来表示,其中 true 表示位是开启的(1),