讀此篇文章,你得先了解變數在暫存器中的賦值過程和生命週期。
左值的意思很簡單,就是左邊的值,在程式語言中,左邊的值一般來說是變數
int var =
10;
此時 var 就是左值。準確的來說左值就是可以被賦值的值,也就是變數。
還是上面那段**,在右邊的就是右值,所以10就是右值,這麼解釋其實並不完全對。因為可能有些人會認為 這種情況也是右值:
int var =10;
int param = var;
有些人會理解成var 此時變成了右值。實際上右值很好理解,可以理解為常量,記憶體中實際的值。簡單的來說,右值是不能被賦值的,比如 10 不能寫成 10 = 11 。
說到右值,就得說右值引用,那麼可以用來做什麼? 看下面這個例子
int
getint()
int&& var =
getint()
;return
0;
如果沒有用右值引用,那麼getint會返回int後會出現乙個臨時變數,臨時變數用來給var賦值。但是此時使用了右值引用,那麼var使用的其實就是getint中的10,會延長臨時變數的生命週期。
我們都知道,在clone的時候,如果類中有指標成員,那麼在做拷貝的時候通常會進行深拷貝,像這種:
class
parent
parent()
:son
(new
son())
;parent
(const parent& parent)
:son
(new
son(parent.son));
//深拷貝
};
這麼做沒有什麼不對,但是此時,你不想讓使用者了解這個類具體的構造過程,所以你寫了個factory,像這樣:
parent getparent()
//使用者使用
parent p =
getparent()
;
這種情況下,資源會被浪費到什麼地步?關閉編譯器優化我們理一下過程
getparent中會構造一次parent但是我就想用factory,不想讓使用者知道這個類具體的構造過程,怎麼辦?了解一下移動構造。getparent在返回時會進行析構,此時還沒有析構
因為析構會導致無法賦值,所以在getparent()會產生乙個臨時物件,所以又呼叫了一次拷貝構造
getparent返回析構。
parent p = 這裡又呼叫一次對臨時物件的拷貝構造
臨時物件析構.
總的來說 進行了1次構造,2次深度拷貝,2次析構,這裡更不用說深拷貝中son的拷貝同樣是2次,如果son的資源占用很大,那麼這種浪費是很可怕的。
在上面深拷貝造成的資源浪費後,我們想到了一種辦法,使用右值引用來呼叫getparent;
parent&& p =
getparent()
;
現在的過程就會是這樣:
getparent 中觸發構造現在只會觸發一次構造、一次深拷貝、一次析構。這裡要說一下常量左值這個由於延長了右值生命週期,不會出現臨時物件,parent&& p = 只會觸發一次拷貝構造
getparent 中的物件析構
通用
引用型別。,universal references
既可以接受左值也可以接收右值,const parent& p = getparent(); 是相同的效果。
還有沒有辦法優化呢?
因為我們使用右值引用後,getparent中的物件被延長生命週期,可以讓我們只進行一次深拷貝於是 我們優化一下parent這個類:但是getparent中的物件,析構還是會觸發son的delete,事實上getparent中的物件我們根本用不上,延長生命週期也只是為了減少深拷貝次數,能不能省去它成員(son)的delete和深拷貝中的new呢?
class
parent
parent()
:son
(new
son())
;parent
(const parent& parent)
:son
(new
son(parent.son));
//深拷貝
//如果是右值引用,我們直接將son指標賦值,不進行深拷貝,只是淺拷貝,並且將原來的son的指標置空
parent
(parent&& parent)
:son
(parent.son)
//或者這麼寫
//parent(parent&& parent)
}
使用:
parent p =
getparent()
;
按照慣例,還是寫一下過程:
getparent 觸發構造,然後返回右值,因為析構後會無法賦值,所以出現乙個臨時變數如果想更節約 可以 const parent& p = getparent();臨時變數 會觸發一次移動構造
getparent 中的物件析構
parent p = 又觸發一次移動構造
臨時物件析構
總的來說 一次構造,2次移動構造,2次析構
移動構造會讓我們省去成員中指標的堆建立和釋放,極大的提高了效能。
事實上目前的編譯器都會進行優化,所以上面的例子其實只會進行一次構造,連拷貝構造都用不上寫這個原因是讓大家清楚右值引用的作用,既然編譯器會優化那麼就毫無用處了嗎?並不是,可以這麼用,下面的例子在編譯器優化後是這樣的:
parent temp =
getparent()
;//只進行一次建構函式
//todo something
//move其實是將左值轉為右值,move只是表明語義,進行移動構造。
parent p = std::
move
(temp)
;//移動構造
如果是t&
這既可以是左值
也可以是右值
,因為這個t
是不確定
的,const class&
(universal references
)也同樣既可以是左值
也可以是右值
,因為const
語義本身就是不可變
,乙個不可變的左值
也可以是右值
。 右值引用,移動構造,移動賦值
1 目的 右值引用,移動構造和移動賦值是在c 11中引入的,其目的是為了提公升 效率 2 使用場景 類中如果有需要申請動態資源的成員,那麼定義移動建構函式和移動賦值運算子可以避免不必要的拷貝工作,從而提公升 效率。移動構造和移動賦值並不會新開闢資源,而是將源物件的一部分或全部資源移交給了新物件。st...
C 右值引用與移動構造
c 中提供了兩種引用方式。左值引用與右值引用。其中右值引用是c 11的新標準新增的內容。所謂的右值引用就是必須繫結到右值的引用。在介紹之前,先說明一下c 中的左值和右值的規定。實際上,最早在c語言中就有了左值和右值之分。最初的左值即指在賦值號左邊的變數,右值指在賦值號右邊的變數。隨著c語言的發展和c...
左值 右值 左值引用 右值引用
2015 06 01 15 07 404人閱讀收藏 舉報 c 11 5 一 c 中的左值和右值 誤區 左值位於等號左邊,右值位於等號右邊。c 11中的定義 左值表示式表示的是乙個物件的身份 在記憶體中的位置 而右值表示式表示的是物件的值 內容 左值和右值都是針對表示式而言的,左值是持久的,右值是短暫...