右值引用的目的主要是為了是減少記憶體拷貝,優化效能。
左值右值
賦值操作符「=」的左側,通常是乙個變數
賦值操作符「=」的右側,通常是乙個常數、表示式、函式呼叫
表示式結束後依然存在的持久化物件
表示式結束時就不再存在的臨時物件
左值可以運用&操作符取得位址
右值(臨時物件)無法取得位址
左值一般有名字
右值一般沒有名字
c++11以前,就有左值引用,簡稱引用,左值引用就像是給變數起了別名,操作這個別名就跟操作原變數名一樣。
int x =20;
int& rx = x;
// 定義左值引用時必須初始化
c++11以前,右值被認為是無用的資源,而c++11引入右值引用,就是為了重用右值。定義右值引用需要使用&&:
int
&& rrx =
200;
//rrx本身是乙個左值,但是裡面存放的是200這個右值的位址,只要rrx仍存在,200這個右值就不會被銷毀。
int& lx = rrx;
//lx等於10,對lx的改變同樣會作用到rrx
右值引用只可以引用右值:
x a;
x f();
x& r1 = a;
// 將r1繫結到a(乙個左值)
x& r2 =f(
);// 錯誤:f()的返回值是右值,無法繫結
x&& rr1 =f(
);// ok:將rr1繫結到臨時變數
x&& rr2 = a;
// 錯誤:不能將右值引用rr2繫結到左值a
移動語義:
c++11引入右值引用的意義就是為了實現移動語義。比如,類的右值是乙個臨時物件,如果沒有被繫結到右值引用上,這個臨時物件在表示式結束時就會被廢棄。所以我們可以在右值被廢棄之前,移走它的資源進行廢物利用,從而避免無意義的複製。其中以同型別的右值構造物件時,需要以右值引用形式傳入引數,然後移走這個臨時物件的資源(以前是複製臨時物件的資源)。
比如用c++實現乙個字串類mystring
,mystring
內部開闢和管理乙個c語言的char *
陣列,這個時候一般都需要自己實現拷貝建構函式和拷貝賦值函式(因為c++預設的拷貝是淺拷貝,只會直接複製指標,但指標這種資源不能共享,不然乙個析構了,另乙個也就完蛋了)
int
main()
cout << mystring::cctor << endl;
}
push_back函式會對傳遞進來的引數進行一次拷貝(呼叫拷貝建構函式),並將其新增到vector中。因此上述**會執行1000
次拷貝建構函式。在每次迭代中執行的操作如下:
呼叫mystring("hello")
構造出臨時物件,這個臨時物件是乙個右值。
把臨時物件複製乙個(呼叫mystring的拷貝建構函式),放到vecstr中。
mystring("hello")
只是臨時物件,拷貝完就沒用了,進行析構釋放。
下一輪又重新構造臨時物件以及拷貝。
最理想的情況下,臨時物件構造出來後,應該直接交給vecstr,而不是把內容複製一遍後析構掉,造成沒有意義的資源申請和釋放操作。而c++11
新增加的移動語義就能夠做到這一點。
要實現移動語義就必須增加兩個函式:移動建構函式和移動賦值建構函式。以移動建構函式為例,對比普通的拷貝建構函式和移動建構函式:
// 普通的拷貝建構函式
mystring
(const mystring& str)
// 移動建構函式
mystring
(mystring&& str)
noexcept
:m_data
(str.m_data)
mystring("hello")
是個臨時物件,是個右值,優先進入移動建構函式而不是拷貝建構函式(移動構造函式引數使用右值引用),它並不是重新分配一塊新的空間、將要拷貝的物件複製過來,而是"偷"了過來,即複製臨時物件的指標,使得自己的指標指向臨時物件的資源,然後將臨時物件的指標修改為nullptr
,這樣臨時析構的時候就無法釋放其資源(否則它把資源釋放了,那偷來的資源就沒有了)。
複製與移動的區別如下,可以看到move構造是直接搶了臨時物件的資源:
對於乙個左值,肯定是呼叫拷貝建構函式了,但是有些左值是區域性變數,生命週期也很短,能不能也移動而不是拷貝呢?c++11
為了解決這個問題,提供了std::move()
方法來將左值轉換為右值,從而方便應用移動語義。我覺得它其實就是告訴編譯器,雖然我是乙個左值,但是不要對我用拷貝建構函式,而是用移動建構函式吧。
更具體的講解和例子看[c++11]我理解的右值引用、移動語義和完美**,這部落格講的很好。
c 11 右值引用
右值引用 是一種復合型別,跟c 的傳統引用很類似。為更準確地區分兩種型別,我們把傳統的c 引用稱為 左值引用 而使用 引用 這一術語時,我們的意思同時包含兩種引用 左值引用和右值引用。右值引用的行為跟左值引用類似,不同之處在於 右值引用可以繫結到臨時量 右值 而 非const的 左值引用卻不能繫結到...
C 11 右值引用
消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率。能夠更簡潔明確地定義泛型函式。1.右值引用 int a a 1 here,a is an lvalue 上述的a就是乙個左值。c 11中左值的宣告符號為 為了和左值區分,右值的宣告符號為 printreference const str...
C 11右值引用
c 11中引入的乙個非常重要的概念就是右值引用。理解右值引用是學習 移動語義 move semantics 的基礎。而要理解右值引用,就必須先區分左值與右值。對左值和右值的乙個最常見的誤解是 等號左邊的就是左值,等號右邊的就是右值。左值和右值都是針對表示式而言的,左值是指表示式結束後依然存在的持久物...