C++右值引用与move和forward函数的使用详解

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 
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(lvalue)

3、foward

move将左值转换为右值,foward可以满足更多的情形

std::forward(t);

  • 当T为左值引用类型时,t将被转换为T类型的左值
  • 当T不是左值引用类型时,t将被转换为T类型的右值

int a = 123;

foward(a); // a转换为左值并返回

foward(a); // a转换为右值并返回

foward(a); // a转换为右值并返回

到此这篇关于C++右值引用与move和forward函数的使用详解的文章就介绍到这了,更多相关C++右值引用内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

你可能感兴趣的:(C++右值引用与move和forward函数的使用详解)