C++右值引用與move和forward函數(shù)的使用詳解
1、右值
1.1 簡(jiǎn)介
首先區(qū)分一下左右值:
- 左值是指存儲(chǔ)在內(nèi)存中、有明確存儲(chǔ)地址(可取地址)的數(shù)據(jù);
- 右值是指可以提供數(shù)據(jù)值的數(shù)據(jù)(不可取地址)
如int a=123;123是右值, a是左值??偟膩碚f 可以對(duì)表達(dá)式取地址(&)就是左值,否則為右值
而C++11 中右值又可以分為兩種:
- 純右值:非引用返回的臨時(shí)變量、運(yùn)算表達(dá)式產(chǎn)生的臨時(shí)變量如a+b、原始字面量和 lambda 表達(dá)式等
- 將亡值:與右值引用相關(guān)的表達(dá)式、返回T&& 類型函數(shù)的返回值
1.2 右值引用
常見的 & 為左值引用、右值引用使用 && 表示
int&& a = 123; int &b = a; int &&c = a;//不合法
如上 a 是對(duì)123的右值引用,但是a本身是左值,其在內(nèi)存中有明確的存儲(chǔ)地址,所以c不能再對(duì)其進(jìn)行左值引用。
1.3 右值引用的意義
可以將資源(堆、系統(tǒng)對(duì)象等)通過淺拷貝從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象這樣就能減少不必要的臨時(shí)對(duì)象的創(chuàng)建、拷貝以及銷毀,可以大幅提高應(yīng)用程序的性能。
#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; } // 移動(dòng)構(gòu)造函數(shù)其實(shí)就是將入?yún)⒌馁Y源賦值給自己,并將入?yún)⒌膶?duì)應(yīng)資源指針制空, 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()會(huì)得到一個(gè)非引用的臨時(shí)對(duì)象,是純右值,如果只有拷貝構(gòu)造函數(shù)就只能再次new一塊區(qū)域去保存該右值的資源,這是因?yàn)椴荒艽_定拷貝構(gòu)造傳入的參數(shù)后面是不是還會(huì)繼續(xù)被使用, 只好進(jìn)行深拷貝。而對(duì)于傳入右值的情況,可以確定右值以后不會(huì)再進(jìn)行訪問,因此可直接將其指針復(fù)給新對(duì)象,將入?yún)⒌膶?duì)應(yīng)指針置為null,防止析構(gòu)造成野指針,避免深拷貝帶來的性能消耗。
由此可見,右值引用具有移動(dòng)語義:將確定后續(xù)不再使用的對(duì)象中的資源轉(zhuǎn)移給新的對(duì)象,雖然左值引用也能夠做到資源轉(zhuǎn)移,但傳入的左值后續(xù)可能還會(huì)被更改和使用,個(gè)人認(rèn)為右值引用恰好做到了這種區(qū)分。
2、move
使用std::move方法可以將左值轉(zhuǎn)換為右值。使用這個(gè)函數(shù)并不能移動(dòng)任何東西,而是和移動(dòng)構(gòu)造函數(shù)一樣都具有移動(dòng)語義,將對(duì)象的狀態(tài)或者所有權(quán)從一個(gè)對(duì)象轉(zhuǎn)移到另一個(gè)對(duì)象,只是轉(zhuǎn)移,沒有內(nèi)存拷貝。
當(dāng)確定一個(gè)變量a后續(xù)不會(huì)再進(jìn)行使用,并且需要將其賦值給另一個(gè)對(duì)象時(shí),可以使用移動(dòng)構(gòu)造來轉(zhuǎn)移資源
Test a;
Test && b = a; // error
上面的操作是不可行的,因?yàn)閍不是一個(gè)右值,要想調(diào)用Test的移動(dòng)構(gòu)造函數(shù),就必須將a這個(gè)左值轉(zhuǎn)變?yōu)橐粋€(gè)右值:使用move() 函數(shù)
Test a;
Test && b = move(a); // ok
std::move 基本等同于一個(gè)類型轉(zhuǎn)換:
static_cast<T&&>(lvalue)
3、foward
move將左值轉(zhuǎn)換為右值,foward可以滿足更多的情形
std::forward<T>(t);
- 當(dāng)T為左值引用類型時(shí),t將被轉(zhuǎn)換為T類型的左值
- 當(dāng)T不是左值引用類型時(shí),t將被轉(zhuǎn)換為T類型的右值
int a = 123;
foward<int&>(a); // a轉(zhuǎn)換為左值并返回
foward<int&&>(a); // a轉(zhuǎn)換為右值并返回
foward<int>(a); // a轉(zhuǎn)換為右值并返回
到此這篇關(guān)于C++右值引用與move和forward函數(shù)的使用詳解的文章就介紹到這了,更多相關(guān)C++右值引用內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C語言模擬實(shí)現(xiàn)簡(jiǎn)單掃雷游戲
這篇文章主要為大家詳細(xì)介紹了C語言模擬實(shí)現(xiàn)簡(jiǎn)單掃雷游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-10-10C語言詳細(xì)分析宏定義與預(yù)處理命令的應(yīng)用
宏定義是用宏名來表示一個(gè)字符串,在宏展開時(shí)又以該字符串取代宏名,這只是一種簡(jiǎn)單的替換。字符串中可以含任何字符,可以是常數(shù),也可以是表達(dá)式,預(yù)處理程序?qū)λ蛔魅魏螜z查,如有錯(cuò)誤,只能在編譯已被宏展開后的源程序時(shí)發(fā)現(xiàn)2022-07-07C++實(shí)現(xiàn)不能被繼承的類實(shí)例分析
這篇文章主要介紹了C++實(shí)現(xiàn)不能被繼承的類實(shí)例分析,對(duì)于C++初學(xué)者而言可以通過本文實(shí)例更好的理解類的原理及運(yùn)用,需要的朋友可以參考下2014-08-08淺談C++類型轉(zhuǎn)化(運(yùn)算符重載函數(shù))和基本運(yùn)算符重載(自增自減)
下面小編就為大家?guī)硪黄獪\談C++類型轉(zhuǎn)化(運(yùn)算符重載函數(shù))和基本運(yùn)算符重載(自增自減)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2017-06-06C++設(shè)計(jì)模式編程中proxy代理模式的使用實(shí)例
這篇文章主要介紹了C++設(shè)計(jì)模式編程中proxy代理模式的使用實(shí)例解析,代理模式可以被歸類為結(jié)構(gòu)型的設(shè)計(jì)模式,代理模式主張為對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問,需要的朋友可以參考下2016-03-03