(P36-P39)右值和右值引用、右值引用的作用以及使用、未定引用类型的推导、右值引用的传递
文章目錄
- 1.右值
- 2. 右值引用
- 3.性能優化
- 4.&& 的特性
- 5.右值引用的傳遞
1.右值
C++11 增加了一個新的類型,稱為右值引用( R-value reference),標記為 &&。
在介紹右值引用類型之前先要了解什么是左值和右值:
- lvalue 是 loactor value 的縮寫,rvalue 是 read value 的縮寫
- 左值是指存儲在內存中、有明確存儲地址 (可取地址)的數據;
- 右值是指可以提供數據值的數據(不可取地址);
通過描述可以看出,區分左值與右值的便捷方法是:可以對表達式取地址(&)就是左值,否則為右值 。
所有有名字的變量或對象都是左值,而右值是匿名的。
- eg:一般情況下,位于 = 前的表達式為左值,位于 = 后邊的表達式為右值。
- 也就是說例子中的 a, b 為左值,520,1314 為右值。a=b 是一種特殊情況,在這個表達式中 a, b 都是左值,因為變量 b 是可以被取地址的,不能視為右值。
C++11 中右值可以分為兩種:一個是將亡值( xvalue, expiring value),另一個則是純右值( prvalue, PureRvalue):
-
純右值:非引用返回的臨時變量、運算表達式產生的臨時變量、原始字面量和 lambda 表達式等
-
將亡值:與右值引用相關的表達式,比如,T&& 類型函數的返回值、 std::move 的返回值等。
-
eg:在上面的語句中,value 是左值,520 是字面量也就是右值。其中 value 可以被引用,但是 520 就不行了,因為字面量都是右值。
2. 右值引用
右值引用就是對一個右值進行引用的類型。因為右值是匿名的,所以我們只能通過引用的方式找到它。
-
無論聲明左值引用還是右值引用都必須立即進行初始化,因為引用類型本身并不擁有所綁定對象的內存,只是該對象的一個別名。
-
通過右值引用的聲明,該右值又“重獲新生”,其生命周期與右值引用類型變量的生命周期一樣,只要該變量還活著,該右值臨時量將會一直存活下去。
-
引用的本質是:void* const 指針常量
-
eg:
- eg:常量引用:引用的值不能被修改;右值引用需要用右值進行初始化;左值初始化右值引用是錯誤的
3.性能優化
在 C++ 中在進行對象賦值操作的時候,很多情況下會發生對象之間的深拷貝,如果堆內存很大,這個拷貝的代價也就非常大,在某些情況下,如果想要避免對象的深拷貝,就可以使用右值引用進行性能的優化。
- eg:
- 測試:
- 解釋:
通過輸出的結果可以看到調用 Test t = getObj(); 的時候調用拷貝構造函數對返回的臨時對象進行了深拷貝得到了對象 t,在 getObj() 函數中創建的對象雖然進行了內存的申請操作,但是沒有使用就釋放掉了。
如果能夠使用臨時對象已經申請的資源,既能節省資源,還能節省資源申請和釋放的時間,如果要執行這樣的操作就需要使用右值引用了,右值引用具有移動語義,移動語義可以將資源(堆、系統對象等)通過淺拷貝從一個對象轉移到另一個對象這樣就能減少不必要的臨時對象的創建、拷貝以及銷毀,可以大幅提高 C++ 應用程序的性能。
- eg:
-
測試:
-
解釋:
通過修改,在上面的代碼給 Test 類添加了移動構造函數(參數為右值引用類型),這樣在進行 Test t = getObj(); 操作的時候并沒有調用拷貝構造函數進行深拷貝,而是調用了移動構造函數,在這個函數中只是進行了淺拷貝,沒有對臨時對象進行深拷貝,提高了性能。
如果不使用移動構造,在執行 Test t = getObj() 的時候也是進行了淺拷貝,但是當臨時對象被析構的時候,類成員指針 int* m_num; 指向的內存也就被析構了,對象 t 也就無法訪問這塊內存地址了。
在測試程序中 getObj() 的返回值就是一個將亡值,也就是說是一個右值,在進行賦值操作的時候如果 = 右邊是一個右值,那么移動構造函數就會被調用。移動構造中使用了右值引用,會將臨時對象中的堆內存地址的所有權轉移給對象t,這塊內存被成功續命,因此在t對象中還可以繼續使用這塊內存。
對于需要動態申請大量資源的類,應該設計移動構造函數,以提高程序效率。需要注意的是,我們一般在提供移動構造函數的同時,也會提供常量左值引用的拷貝構造函數,以保證移動不成還可以使用拷貝構造函數。
4.&& 的特性
C++ 中,并不是所有情況下 && 都代表是一個右值引用,具體的場景體現在模板和自動類型推導中,
-
如果是模板參數,需要指定為 T&&,如果是自動類型推導,需要指定為 auto &&,在這兩種場景下 && 被稱作未定的引用類型。
-
另外還有一點需要額外注意,const T&& 表示一個右值引用,不是未定引用類型
-
eg:在函數模板中使用 &&
例子中函數模板進行了自動類型推導,需要通過傳入的實參來確定參數 param 的實際類型。
- eg:
由于上述代碼中存在 T&& 或者 auto&& 這種未定引用類型,當它作為參數時,有可能被一個右值引用初始化,也有可能被一個左值引用初始化,在進行類型推導時右值引用類型(&&)會發生變化,這種變化被稱為引用折疊。
在 C++11 中引用折疊的規則如下:
-
通過右值推導 T&& 或者 auto&& 得到的是一個右值引用類型
-
通過非右值(右值引用、左值、左值引用、常量右值引用、常量左值引用)推導 T&& 或者 auto&& 得到的是一個左值引用類型
-
eg:
5.右值引用的傳遞
- eg:
-
測試:
-
解釋:
根據測試代碼可以得知,編譯器會根據傳入的參數的類型(左值還是右值)調用對應的重置函數(printValue);
函數 forward () 接收的是一個右值,但是在這個函數中調用函數 printValue () 時,參數 k 變成了一個命名對象,編譯器會將其當做左值(當成左值引用更好理解些?)來處理。
總結&&使用:
左值和右值是獨立于他們的類型的,右值引用類型可能是左值也可能是右值。
編譯器會將已命名的右值引用視為左值,將未命名的右值引用視為右值。
auto&&或者函數參數類型自動推導的T&&是一個未定的引用類型,它可能是左值引用也可能是右值引用類型,這取決于初始化的值類型(上面有例子)。
通過右值推導 T&& 或者 auto&& 得到的是一個右值引用類型,其余都是左值引用類型。
- 參考:右值引用
總結
以上是生活随笔為你收集整理的(P36-P39)右值和右值引用、右值引用的作用以及使用、未定引用类型的推导、右值引用的传递的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 搜索引擎蜘蛛的功能与应用
- 下一篇: 人工智能方面有什么创业项目_人工智能创业