C++11中的一個新特性就是右值引用和左右值之間的轉移語義。在此之前,C++中右值引用是不被允許的。增加右值引用和轉移語義這兩個特性能夠使代碼更加簡潔高效。
右值引用 (Rvalue Referene) 是 C++ 新標準 (C++11, 11 代表 2011 年 ) 中引入的新特性 , 它實現了轉移語義 (Move Sementics) 和精確傳遞 (Perfect Forwarding)。它的主要目的有兩個方面: (1) 消除兩個對象交互時不必要的對象拷貝,節省運算存儲資源,提高效率。 (2) 能夠更簡潔明確地定義泛型函數。
C++( 包括 C) 中所有的表達式和變量要么是左值,要么是右值。通俗的左值的定義就是非臨時對象,那些可以在多條語句中使用的對象。所有的變量都滿足這個定義,在多條代碼中都可以使用,都是左值。右值是指臨時的對象,它們只在當前的語句中有效。請看下列示例 : 簡單的賦值語句: int i=233; 在這條語句中,i 是左值,233是臨時值即右值。在此后的程序中,i 可以被引用,233不可以引用,并且立即數都是右值。 在 C++11 之前,右值是不能被引用的,最大限度就是用常量引用綁定一個右值,如 :
一般情況下,右值不能被改動。如果要改動可以這樣:
F().set().get();F是一個類,set 是一個函數為 F中的一個變量賦值,get 用來取出這個變量的值。在這句中,F() 生成一個臨時對象,就是右值,set() 修改了變量的值,也就修改了這個右值。這樣很麻煩,性能反而不能提升還降低了代碼的簡潔性。因此C++11中增加的右值引用能夠方便地解決實際工程中的問題,實現非常有吸引力的解決方案。
左值的聲明符號為”&”, 右值的聲明符號為”&&”。 示例程序 :
void PRocessValue(int &i,int && j){ printf("%d %d/n", i,j);}void main(){ int a = 2; processValue(a, 233);}程序運行后輸出2 233,但是如果將a 和 233調換位置,編譯就會出現錯誤,因為非常量引用必須為左值而右值引用也無法綁定到左值。從運行的結果看,臨時對象是作為右值處理的,但是如果臨時對象通過一個接受右值的函數傳遞給另一個函數時,就會變成左值,因為這個臨時對象在傳遞過程中,變成了命名對象。 實例程序:
void processValue(int & j){ printf("L%d", j);}void processValue(int && j){ printf("R%d", j);}void processTest(int &&i){ processValue(i);}void FunctionView(){ int a = 2; processTest(2);}程序運行后輸出的是L2。
右值引用是用來支持轉移語義的。轉移語義可以將資源 ( 堆,系統對象等 ) 從一個對象轉移到另一個對象,這樣能夠減少不必要的臨時對象的創建、拷貝以及銷毀,能夠大幅度提高 C++ 應用程序的性能。臨時對象的維護 ( 創建和銷毀 ) 對性能有嚴重影響。 轉移語義是和拷貝語義相對的,可以類比文件的剪切與拷貝,當我們將文件從一個目錄拷貝到另一個目錄時,速度比剪切慢很多。 通過轉移語義,臨時對象中的資源能夠轉移其它的對象里。 在現有的 C++ 機制中,我們可以定義拷貝構造函數和賦值函數。要實現轉移語義,需要定義轉移構造函數,還可以定義轉移賦值操作符。對于右值的拷貝和賦值會調用轉移構造函數和轉移賦值操作符。如果轉移構造函數和轉移拷貝操作符沒有定義,那么就遵循現有的機制,拷貝構造函數和賦值操作符會被調用。
標準庫提供了函數 std::move,這個函數以非常簡單的方式將左值引用轉換為右值引用。編譯器只對右值引用才能調用轉移構造函數和轉移賦值函數,而所有命名對象都只能是左值引用,如果已知一個命名對象不再被使用而想對它調用轉移構造函數和轉移賦值函數,也就是把一個左值引用當做右值引用來使用
void processValueL(int & j){ printf("L%d", j);}void processValueR(int && j){ printf("R%d", j);}void main(){ int a = 2; processValueL(a); processValueR(std::move(a));}運行程序后輸出L2R2。 std::move的一個重要作用就是提高數據交換的性能,使用std::move后就能夠避免使用拷貝了。
void FunSwap(int a, int b){ int tmp(std::move(a)); a = std::move(b); b = std::move(tmp);}移動比拷貝速度要快很多,這對與那些需要大量數據交換的場合來說很有用。
右值引用,表面上看只是增加了一個引用符號,但它對 C++ 軟件設計和類庫的設計有非常大的影響。它既能簡化代碼,又能提高程序運行效率。每一個 C++ 軟件設計師和程序員都應該理解并能夠應用它。我們在設計類的時候如果有動態申請的資源,也應該設計轉移構造函數和轉移拷貝函數。在設計類庫時,還應該考慮 std::move 的使用場景并積極使用它。在使用之前,需要檢查一下編譯器的支持情況。
新聞熱點
疑難解答