c 右值引用

2022-05-28 04:03:10 字數 4455 閱讀 1864

簡單的定義來說,能夠放在賦值等號左邊的就是左值,反之則是右值(所有表示式不是左值就是右值,左右值不存在交集)——但是這個解釋實在有點雞肋。下面對定義結合例子做些補充。

1

inti;

2 i = 2

; //合法

32 = i; //非法

i 是乙個變數,有記憶體位址用於儲存變數,是左值。2 在記憶體中無記憶體位址是右值。

1 #include2

intmain()

3; int r = ;

8int* i = arr; //

ok9 std::cout << arr << std::endl; //

006ff808

10arr = r;

//error

11 *(arr) = 10;//

ok12 }

第6行,t 是左值,但是通過 & 可以將左值轉化成右值,因為 &t 表示記憶體中乙個位址,同第乙個例子中的數字2一樣,都是右值;

同理第5行錯誤,因為 t+1 是值,不是變數,所以無法通過 &(t+1) 取出乙個位址當作右值。

第八行,arr 作為右值(注意 arr 是陣列記憶體位址)

第九行可以看到列印的 arr 代表的位址內容

第十行也就必定是錯誤的(是右值就不可能作為左值),r 是陣列位址是右值,但是 arr 也是右值。

而第11行,arr 通過 *轉換使得 *arr 成為左值 (在記憶體中有儲存數的空降)。其中扮演關鍵角色的就是 * 解引用。

通過下面的例子來理解:

1 #include2 #include

3using

namespace

std;

4structa5

1011

//拷貝賦值運算子

12 a& operator=(const a&tmp) 19}

20 cout << "

copy assignment 等於...

"<

21return *this;22

}2324//

拷貝建構函式

25 a(const a&tmp)

30 cout << "

copy construct...

"<

3233

//析構函式

結果上看,應該都比較好理解。

其中第三處是要引出的重點,可以看到應該使用了 d = a(10),這條語句,a() 是乙個臨時變數,在完成 construct -> copy assignment 之後就進行了 destructor。我們來討論下這一句賦值語句的內部過程,a(10) 呼叫建構函式並申請了記憶體空間,然後去實現拷貝賦值運算子,臨時變數a(10)的內容【copy】給 d 變數,隨後 a(10) 被銷毀。而我們知道在 copy assignment (拷貝賦值運算子)方法中,我們又為 d 變數同樣申請了 大小為10的陣列空間,這就出現了一定的「低效處理」。不妨這樣想,如果我們將a(10)申請的空間,通過拷貝賦值運算子,直接將這個空間給變數d,這樣d就不用再去申請空間(反正a(10)的空間申請了,馬上就會釋放。相當於白白多申請和釋放了一次,不如直接將a(10)的空間轉移到d變數名下)。

這就引入了右值引用【右值引用使用的符號是&&】,如下例子:

1 #include2 #include

3using

namespace

std;

4structa5

1011

//拷貝賦值運算子

12 a& operator=(const a&tmp) 19}

20 cout << "

copy assignment 等於...

"<

21return *this;22

}2324//

使用右值引用的拷貝賦值運算子

25 a& operator=(a&&tmp)

34 cout << "

copy assignment 等於2...

"<

35return *this;36

}3738//

拷貝建構函式

39 a(const a&tmp)

44 cout << "

copy construct...

"<

4647

//析構函式

可以看到通過使用右值引用,執行一樣的 d = a(5)操作會呼叫右值引用的拷貝運算子方法。這樣寫就能達到如下效果:a(5)申請了臨時空間,但是這空間通過拷貝運算子方法(右值引用)直接被 d 變數接管,d 變數無需再去申請空間了。注意:第31,32行**不能不寫,經過28,29行之後,d 變數和 a(5) 臨時變數的指標都指向了同一塊記憶體,而之後 a(5) 將被銷毀,所以需要將 a(5) 的指標置為 nullptr,但這部分記憶體由 d 物件接管。注意:這裡看上去好像少了臨時變數 a(5) destructor(沒有列印),其實物件 a(5) 也是執行了析構,析構時釋放這個類——也就是臨時a(5)這個類,但是因為我析構函式有個if條件,所以只是看上去沒有列印罷了,反正依舊會執行析構。

我們先看下面的例子:

1 #include2

using

namespace

std;

3void f(int&&i) 67

void f(int&i)

1011

int main()

12

因為我們已經知道了如果分辨左值和右值,所以輸出應該都在意料之內。

但是請比較第15行 和 第18行,可以看到同乙個變數 i 怎麼還能輸出不同的結果?其中的關鍵就是 std::move(),請注意:std::move() 並不是如上文提到的記憶體接管,而是將 左值->變換成-> 右值。

注意:如果沒有 21~24行的移動建構函式,則結果如下,再構造 test b 的時候會使用賦值建構函式

改變main 函式中的兩句話:

完結,撒花。*★,°*:.☆( ̄▽ ̄)/$:*.°★* 。

c 左值 右值 右值引用 左值引用

c 裡一切值必須屬於左值 右值兩者之一。左值 一切變數 包括用const修飾的變數 物件 包括引用都屬於左值 右值 一切字面值 可以是巨集 臨時無名物件 函式返回值 表示式 如a n 說明一下 函式返回值,返回的是某乙個型別的值,並不是返回變數。左值並不是說能放在 左邊的值就是左值 雖然用const...

c 左值 右值 左值引用 右值引用

在c語言中,左值認為是賦值語句的左側,右值認為是賦值語句的右側。在c 中,意義稍有不同。c 中,每乙個表示式會產生乙個左值或者右值,相應的,該表示式也就被稱作 左值表示式 右值表示式 乙個左值表示式的求值結果是乙個物件或者是乙個函式。左值可以當右值使用,而右值不能當左值使用。c prime 中這麼簡...

C 左值 右值 左值引用 右值引用

就變數而言,對於一些變數,我們只會讀取並使用它們的值,而不會改變他們的值 唯讀 對於其餘的變數,我們既會讀取它們的值,有的時候還會改變它們的值 讀寫 這是很常見的。在c 中,前一種變數稱為右值,後一種變數稱為左值,例如 int a 1 a是左值,1是右值稍稍不同的一點是,在c 中,乙個變數是左值還是...