1.移動語義
c++11新引入了右值引用和移動語義兩個概念。
1.1 右值引用
C++(包括C)中所有的表達式和變量要么是左值,要么是右值。通俗的左值的定義就是非臨時對象,可以在多條語句中使用的對象。右值是指臨時的對象,它們只在當前的語句有效。在C++11之前,右值是不能被引用的。如int &a =1; //無法從“int”轉化為“int&”。 我們最多只能用常量引用來綁定一個右值。因為規定不允許修改右值。在C++11中,可以引用右值,使用&&實現:int &&a = 1; //OK.
在理解右值引用之前,先理解臨時對象(臨時變量)的概念。
臨時對象是編譯器在編譯代碼過程中為了實現某些代碼可能會產生出一些臨時對象來滿足一些效果。產生臨時變量的地方可能有以下幾種:
- 不同類型(對象)變量的轉換
- 函數以pass by value傳遞參數的時候
- 表達式求值中
規定不允許修改臨時變量,所以在沒有引入移動構造函數的時候,拷貝構造的參數為const &類型,以支持通過臨時對象來構造對象。臨時對象在函數返回后很快就會銷毀,為了能物盡其用而達到節省時間(進行對象拷貝)的效果,考慮將臨時對象納為己有,這樣能節省內存開辟和繁瑣的賦值時間。
如:
MyString(……):_ptr(mystr._ptr)
{
mystr._ptr = NULL;
}
將_ptr指向的真正的字符串的所有權拿過來了。括號里空出來的參數應該是臨時對象,在c++1.0中,無法判斷什么時候是臨時對象。為此,c++11引入了一個新的概念——右值引用(&&)。右值引用專門用於引用右值(臨時對象、匿名對象(即沒有名字的對象))。
所以上述構造函數可改寫為:
MyString(MyString &&mystr):_ptr(mystr._ptr)
{
myptr._ptr = NULL;
}
當傳入臨時變量(右值)時,編譯器會調用MyString(MyString &&mystr)版本。在函數中,臨時對象的指針置為NULL,這很重要,防止臨時對象析構時候銷毀字符串,使之成為懸掛指針。
2.拷貝構造函數與移動構造函數
拷貝構造是開辟一個新空間然后對臨時對象(函數參數)進行深度拷貝,返回該新對象。
移動構造不另外開辟新空間,直接偷走臨時對象的內存空間,占為己有。
移動構造的優點:節省了開辟內存與賦值的時間。
3.移動構造函數
程序員提供移動構造函數(參數為右值引用的構造函數)使得當臨時變量構造新對象時可以提供較好的優化。提供移動構造函數在某些情況下對性能是極大的提供,尤其是對於需要深拷貝的對象來說。當這一類對象配合標准庫的容器的時候(vector、deque),如果提供移動構造函數,將會在容器內存重分配時帶來極大效率的優化。
4.移動構造函數的調用
用到臨時對象(右值)的時候就會執行移動語義。除了編譯器創建的臨時對象作為右值外,使用std::move也能得到一個左值的右值引用。