左值和右值判斷:
1)可位於賦值號(=)左側的表示式就是左值;反之,只能位於賦值號右側的表示式就是右值。
2)有名稱的、可以獲取到儲存位址的表示式即為左值;反之則是右值。
例如:
int i =10;
10= i;
錯誤,10為右值,不能當左值用
int j =20;
j = i;
i和j都是左值,但是i可以當右值用
以上面定義的變數 i、j 為例,i 和 j是變數名,且通過 &a 和 &b 可以獲得他們的儲存位址,因此 a 和 b 都是左值;反之,字面量10、20,它們既沒有名稱,也無法獲取其儲存位址(字面量通常儲存在暫存器中,或者和**儲存在一起),因此10、20 都是右值。
注意,上面 2 種判定方法只適用於大部分場景。
int i =10;
int&a = i;
int&b =10;
//錯誤
const
int&c =10;
//正確
常規的我們使用的乙個&表示左值引用,普通左值引用只能接收左值,不能接收右值,但是常量左值引用既可以接收左值也可以接收右值。
int j =10;
int&&d =10;
int&&e = j;
//錯誤
兩個&表示右值引用,常規右值引用可以用來接收右值,但是不能用來接收左值
上圖描述了左值引用和右值引用可以接收的資料型別,其中非常量的右值引用常用來進行移動建構函式和完美**。
看下面這個場景:
#include
using
namespace std;
class
demo
//拷貝建構函式
demo
(const demo &d)
:num
(new
int(
*d.num))~
demo()
cout <<
"class destruct!"
<< endl;
}private
:int
*num;};
demo get_demo()
intmain()
可以看到,該程式執行呼叫了兩次拷貝建構函式,第一次是在get_demo函式中,第二次是在a物件初始化的時候,並且這兩次拷貝都是深拷貝,在linux可以看看執行結果:
使用該命令編譯檔案,否則看不到完整輸出結果
試想一下,如果兩次深拷貝過程中在堆上申請了大量記憶體空間,會浪費大量時間,程式效率低下,那麼如何優化這個問題呢,其實仔細想一下,這兩次深拷貝過程生成的都是匿名物件,無法通過&獲取位址,因此它是乙個右值,可以通過引入移動建構函式來優化程式。
#include
using
namespace std;
class
demo
demo
(const demo &d)
:num
(new
int(
*d.num)
)//新增移動建構函式
demo
(demo &&d)
:num
(d.num)
~demo()
cout <<
"class destruct!"
<< endl;
}private
:int
*num;};
demo get_demo()
intmain()
檢視上面程式的執行結果:
可以看到兩次深拷貝的過程都是通過移動建構函式來完成的,在移動建構函式中,引數是乙個demo的右值引用,並且直接將新生成的物件的指標成員指向匿名物件所申請的堆空間,將匿名物件的成員指標置空,這樣就防止每次呼叫拷貝建構函式向堆上申請新的記憶體空間了,大大提高了效率。
所謂移動語義,指的就是以移動而非深拷貝的方式初始化含有指標成員的類物件。簡單的理解,移動語義指的就是將其他物件(通常是臨時物件)擁有的記憶體資源「移為已用」。
預設情況下,左值初始化同類物件只能通過拷貝建構函式完成,如果想呼叫移動建構函式,則必須使用右值進行初始化。c++11 標準中為了滿足使用者使用左值初始化同類物件時也通過移動建構函式完成的需求,新引入了 std::move() 函式,它可以將左值強制轉換成對應的右值,由此便可以使用移動建構函式。
#include
using
namespace std;
class
movedemo
movedemo
(const movedemo &d)
:num
(new
int(
*d.num)
)//新增移動建構函式
可以看到初始化demo3的過程中呼叫的是移動建構函式,原因是使用move將demo左值強制轉化為右值。
右值引用 move函式
為了支援移動操作,新標準引入了一種新的引用型別 右值引用 rvalue reference 所謂右值引用就是必須繫結到右值的引用。我們通過 而不是 來獲得右值引用,右值引用有乙個重要的性質 只能繫結到乙個將要銷毀的物件。因此,我們可以自由地將乙個右值引用的資源 移動 到另乙個物件中。一般而言,乙個左...
右值引用和move語義
標籤 c 11 c lvalue rvalue 2013 10 04 20 49 2909人閱讀收藏 舉報 c 6 目錄 lvalue 具有儲存性質的物件,即lvalue物件,是指要實際占用記憶體空間 有記憶體位址的那些實體物件,例如 變數 variables 函式 函式指標等。rvalue 相比較...
右值引用與移動建構函式 移動賦值
有一陣子沒看c 了,翻開c primer又陌生了一些,想了想引用,於是乎來看了下右值引用。int a 5 int b a 這是左值引用,若我們這樣修改 int a 5 int b a 1 編譯器會報錯 非常量引用的初始值必須為左值。也就是右邊的a 1是常量,常量給非常量引用賦值就報錯。我們可以這樣修...