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

标签:C++,右值引用,move,forward
0
投稿

猜你喜欢

  • Android录制按钮源码解析

    2022-06-24 19:24:44
  • Java concurrency集合之 CopyOnWriteArrayList_动力节点Java学院整理

    2022-10-27 05:03:36
  • java并发编程专题(四)----浅谈(JUC)Lock锁

    2021-11-30 00:20:23
  • Android开发笔记之:一分钟学会使用Logcat调试程序的详解

    2022-07-19 05:17:22
  • Android启动页优化之实现应用秒开

    2021-05-27 23:51:32
  • 解决SpringBoot webSocket 资源无法加载、tomcat启动报错的问题

    2021-07-28 05:06:42
  • Java基本数据类型与类型转换实例分析

    2021-07-13 14:41:29
  • Logger.error打印错误异常的详细堆栈信息

    2022-01-06 23:03:04
  • 使用Java操作TensorFlow的方法

    2023-11-27 21:31:38
  • 聊聊@RequestBody和Json之间的关系

    2023-11-27 03:31:45
  • Java实现的求逆矩阵算法示例

    2023-05-02 03:02:56
  • Mybatis防止sql注入原理分析

    2023-08-09 22:54:44
  • Java详解IO流创建读取与写入操作

    2023-01-23 01:50:28
  • java控制台输出图书馆管理系统

    2022-06-13 01:29:29
  • Hibernate的一对一,一对多/多对一关联保存的实现

    2022-03-13 12:41:08
  • Android实现布局全屏

    2023-11-29 06:44:13
  • Android 后台发送邮件示例 (收集应用异常信息+Demo代码)

    2022-06-24 16:31:06
  • SpringBoot雪花算法主键ID传到前端后精度丢失问题的解决

    2022-07-18 02:30:47
  • BufferedInputStream(缓冲输入流)详解_动力节点Java学院整理

    2022-01-08 07:02:41
  • 使用springboot打包成zip部署,并实现优雅停机

    2021-08-04 20:45:01
  • asp之家 软件编程 m.aspxhome.com