c++對於左值和右值沒有標準定義,但是有乙個被廣泛認同的說法:
可見立即數,函式返回的值等都是右值;而非匿名物件(包括變數),函式返回的引用,const物件等都是左值。
從本質上理解,建立和銷毀由編譯器幕後控制,程式設計師只能確保在本行**有效的,就是右值(包括立即數);而使用者建立的,通過作用域規則可知其生存期的,就是左值(包括函式返回的區域性變數的引用以及const物件)。
先看一下傳統的左值引用
int a = 10;int &b = a; //
定義乙個左值引用變數
b = 20; //
通過左值引用修改引用記憶體的值
左值引用在彙編層面其實和普通的指標是一樣的;定義引用變數必須初始化,因為引用其實就是乙個別名,需要告訴編譯器定義的是誰的引用。
int &var = 10;
constint &var = 10;
使用常引用來引用常量數字10,因為此刻記憶體上產生了臨時變數儲存了10,這個臨時變數是可以進行取位址操作的,因此var引用的其實是這個臨時變數,相當於下面的操作:
constint temp = 10
; const
int &var = temp;
根據上述分析,得出如下結論:
定義右值引用的格式如下:
型別 && 引用名 = 右值表示式;
右值引用是c++ 11新增的特性,所以c++ 98的引用為左值引用。右值引用用來繫結到右值,繫結到右值以後本來會被銷毀的右值的生存期會延長至與繫結到它的右值引用的生存期。
int &&var = 10;
在彙編層面右值引用做的事情和常引用是相同的,即產生臨時量來儲存常量。但是,唯一 一點的區別是,右值引用可以進行讀寫操作,而常引用只能進行讀操作。
右值引用的存在並不是為了取代左值引用,而是充分利用右值(特別是臨時物件)的構造來減少物件構造和析構操作以達到提高效率的目的。
用c++實現乙個簡單的順序棧:
classstack
//析構
~stack()
//拷貝構造
stack(const stack &src)
:msize(src.msize), mtop(src.mtop)
}//賦值過載
stack& operator=(const stack &src)
return *this
; }
intgetsize()
private
:
int *mpstack;
intmtop;
intmsize;
};stack getstack(stack &stack)
intmain()
執行結果如下(編譯時要加上-fno-elide-constructors,要不然編譯器會對其進行優化得到不同的執行結果):
stack(int) //構造sstack(int) //
構造tmp
stack(const stack&) //
tmp拷貝構造main函式棧幀上的臨時物件
~stack() //
tmp析構
operator= //
臨時物件賦值給s
~stack() //
臨時物件析構
~stack() //
s析構
注意:在執行 s = getstack(s); 這條語句的時候,其實包含了兩個操作,首先把 tmp 拷貝給乙個臨時變數,然後把這個臨時變數的值賦給 s
在類中包含有 new 分配的成員時,一般要採用深拷貝,因此在上面為類提供了自定義的拷貝建構函式和賦值運算子過載函式,並且這兩個函式內部實現都是非常的耗費時間和資源(首先開闢較大的空間,然後將資料逐個複製),我們通過上述執行結果發現了兩處使用了拷貝構造和賦值過載,分別是tmp拷貝構造main函式棧幀上的臨時物件、臨時物件賦值給s,其中tmp和臨時物件都在各自的操作結束後便銷毀了,使得程式效率非常低下。
那麼我們為了提高效率,是否可以把tmp持有的記憶體資源直接給臨時物件?是否可以把臨時物件的資源直接給s?
在c++11中,我們可以解決上述問題,方式是提供帶右值引用引數的拷貝建構函式和賦值運算子過載函式.
//帶右值引用引數的拷貝建構函式
stack(stack &&src)
:msize(src.msize), mtop(src.mtop)
//帶右值引用引數的賦值運算子過載函式
stack& operator=(stack &&src) // 這裡必須要用右值引用,因為 getstack 返回值是乙個右值
執行結果如下:
stack(int) //構造sstack(int) //
構造tmp
stack(stack&&) //
呼叫帶右值引用的拷貝建構函式,直接將tmp的資源給臨時物件
~stack() //
tmp析構
operator=(stack&&) //
呼叫帶右值引用的賦值運算子過載函式,直接將臨時物件資源給s
~stack() //
臨時物件析構
~stack() //
s析構
程式自動呼叫了帶右值引用的拷貝建構函式和賦值運算子過載函式,使得程式的效率得到了很大的提公升,因為並沒有重新開闢記憶體拷貝資料。
mpstack = src.mpstack;
可以直接賦值的原因是臨時物件即將銷毀,不會出現淺拷貝的問題,我們直接把臨時物件持有的資源賦給新物件就可以了。
所以,臨時量都會自動匹配右值引用版本的成員方法,旨在提高記憶體資源使用效率。
帶右值引用引數的拷貝構造和賦值過載函式,又叫移動建構函式和移動賦值函式,這裡的移動指的是把臨時量的資源移動給了當前物件,臨時物件就不持有資源,為nullptr了,實際上沒有進行任何的資料移動,沒發生任何的記憶體開闢和資料拷貝。
右值引用的優點是可以通過引用右值,把馬上就要銷毀的右值資源移動給需要這個資源的值,減少資源的拷貝,提高效率。
左值和右值,左值引用和右值引用
左值 lvalue 這一術語 於c語言,用來指代那些可以用在賦值表示式左側的東西,具名物件 在棧和堆上分配的物件,或者其他物件的成員,總之就是又確定儲存空間的東西。而術語右值 rvalue 也是源自c語言,指的是只能在賦值表示式右側出現的東西,如字面值和臨時物件。只能繫結到左值,不能繫結到右值的引用...
左值 右值 左值引用 右值引用
2015 06 01 15 07 404人閱讀收藏 舉報 c 11 5 一 c 中的左值和右值 誤區 左值位於等號左邊,右值位於等號右邊。c 11中的定義 左值表示式表示的是乙個物件的身份 在記憶體中的位置 而右值表示式表示的是物件的值 內容 左值和右值都是針對表示式而言的,左值是持久的,右值是短暫...
左值 左值引用 右值 右值引用
1 左值和右值的概念 左值是可以放在賦值號左邊可以被賦值的值 左值必須要在記憶體中有實體 右值當在賦值號右邊取出值賦給其他變數的值 右值可以在記憶體也可以在cpu暫存器。乙個物件被用作右值時,使用的是它的內容 值 被當作左值時,使用的是它的位址。2 引用 引用是c 語法做的優化,引用的本質還是靠指標...