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 <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轉換為右值並返回

到此這篇關於C++右值引用與move和forward函數的使用詳解的文章就介紹到這瞭,更多相關C++右值引用內容請搜索WalkonNet以前的文章或繼續瀏覽下面的相關文章希望大傢以後多多支持WalkonNet!

推薦閱讀: