C 11 左值引用與右值引用

2021-09-06 09:34:30 字數 3749 閱讀 2720

在 c++11 的新標準中,出現了「右值引用」的說法,既然有了右值引用,那麼傳統的引用也就叫做左值引用。

右值引用 (rvalue referene) 是 c++ 新標準 (c++11, 11 代表 2011 年 ) 中引入的新特性 , 它實現了轉移語義 (move sementics) 和精確傳遞 (perfect forwarding)。它的主要目的有兩個方面:

消除兩個物件互動時不必要的物件拷貝,節省運算儲存資源,提高效率。

能夠更簡潔明確地定義泛型函式。

c++( 包括 c) 中所有的表示式和變數要麼是左值,要麼是右值。通俗的左值的定義就是非臨時物件,那些可以在多條語句中使用的物件。所有的變數都滿足這個定義,在多條**中都可以使用,都是左值。右值是指臨時的物件,它們只在當前的語句中有效。請看下列示例 :

簡單的賦值語句

如:int i = 0;

在這條語句中,i 是左值,0 是臨時值,就是右值。在下面的**中,i 可以被引用,0 就不可以了。立即數都是右值。

右值也可以出現在賦值表示式的左邊,但是不能作為賦值的物件,因為右值只在當前語句有效,賦值沒有意義。

如:((i>0) ? i : j) = 1;

在這個例子中,0 作為右值出現在了」=」的左邊。但是賦值物件是 i 或者 j,都是左值。

在 c++11 之前,右值是不能被引用的,最大限度就是用常量引用繫結乙個右值,如 :

const int &a = 1;

在這種情況下,右值不能被修改的。但是實際上右值是可以被修改的,如 :

t().set().get();

t 是乙個類,set 是乙個函式為 t 中的乙個變數賦值,get 用來取出這個變數的值。在這句中,t() 生成乙個臨時物件,就是右值,set() 修改了變數的值,也就修改了這個右值。

既然右值可以被修改,那麼就可以實現右值引用。右值引用能夠方便地解決實際工程中的問題,實現非常有吸引力的解決方案。

本小節,談「引用」與「左值引用」同義。

引用必須在定義時進行初始化被引用變數名可以是結構變數成員,如s.m函式引數可以是引用型別,表示函式的形式引數與實際引數是同乙個變數,改變形參將改變實參

函式返回值可以是引用型別,但不得是函式的臨時變數。

引用的定義格式:

資料型別& 變數名稱 = 被引用變數名稱;

int a;

int& ref = a;

引用的性質:

引用的重要用途:

常量引用:既能引用常量,不能通過引用改變目標物件值;引用本身也不能改變引用物件

右值引用是 c++11 新標準裡面新增加的一種引用型別。

右值引用:

匿名變數(臨時變數)的別名:

型別名 && 引用名 表示式;
例如:

int

&& sum =3+

4;//初值是3 + 4 這個表示式

float

&& res =

returnrvalue

(f1,f2)

;

右值引用的典型應用是在函式引數中,例如:

void

acceptrvalueref

(t&& s)

目的是:減少函式拷貝開銷

左值的宣告符號為&, 為了和左值區分,右值的宣告符號為&&

#include

using

namespace std;

void

process_value

(int

&i)void

process_value

(int

&&i)

intmain()

執行結果:

lvalue processed: 0

rvalue processed: 1

process_value 函式被過載,分別接受左值和右值。由輸出結果可以看出,臨時物件是作為右值處理的。

但是如果臨時物件通過乙個接受右值的函式傳遞給另乙個函式時,就會變成左值,因為這個臨時物件在傳遞過程中,變成了命名物件。比如:

#include

using

namespace std;

void

process_value

(int

&i)void

process_value

(int

&&i)

void

forward_value

(int

&&i)

intmain()

結果:

lvalue processed: 0

rvalue processed: 1

lvalue processed: 2

雖然 2 這個立即數在函式 forward_value 接收時是右值,但到了 process_value 接收時,變成了左值。

前面說了那麼多,那麼右值引用的意義到底體現在**呢,明明我不用引用也可以實現,為什麼要用右值引用呢,因為在對效能要求較高時,我們必須減少不必要的記憶體消耗,節省儲存資源,比如以下例子:

#include

using

namespace std;

int g_constructcount =0;

int g_copyconstructcount =0;

int g_destructcount =0;

struct aa(

const a &a)~a

()};

a geta()

intmain()

為了觀察清楚這個程式真正的變化,再編譯的時候加上-fno-elide-constructors關閉返回值優化效果。

輸出結果:

construct: 1

copy construct: 1

destruct: 1

copy construct: 2

destruct: 2

destruct: 3

很清楚的可以看到,在沒有返回值優化的情況下,拷貝構造函式呼叫了兩次,一次是geta()函式內部建立的物件返回出來構造乙個臨時物件產生的,另一次是在main函式中構造a物件產生的。第二次的destruct是因為臨時物件在構造a物件之後就銷毀了。

那麼我們把這個程式稍微改改,如果用右值引用來繫結函式返回值的話,結果會是什麼樣子呢?首先修改**

int

main()

輸出結果:

construct: 1

copy construct: 1

destruct: 1

destruct: 2

通過右值引用,比之前少了一次拷貝構造和一次析構,原因在於右值引用繫結了右值,讓臨時右值的生命週期延長了。

所以,通過右值引用,可以讓乙個臨時物件重獲新生,在它完成任務時不用被銷毀,而是生存期與引用他的那個變數一樣長

以及乙個知乎問題:

C 11 左值引用與右值引用

左值,右值 英文並不是left value和right value,而是指記憶體儲存訪問的方式,左值有固定記憶體訪問位址 例如有名字的變數 右值沒有固定的記憶體訪問位址 例如函式的返回值 右值應用的作用 避免老的標準 現無謂的效能消耗,把無記憶體訪問的生命週期延長。在 c 11 的新標準中,出現了 ...

C 11 左值 右值 右值引用詳解

在c 11中所有的值必屬於左值 右值兩者之一,右值又可以細分為純右值 將亡值。在c 11中可以取位址的 有名字的就是左值,反之,不能取位址的 沒有名字的就是右值 將亡值或純右值 舉個例子,int a b c,a 就是左值,其有變數名為a,通過 a可以獲取該變數的位址 表示式b c 函式int fun...

C 11 左值 右值 右值引用詳解

在c 11中所有的值必屬於左值 右值兩者之一,右值又可以細分為純右值 將亡值。在c 11中可以取位址的 有名字的就是左值,反之,不能取位址的 沒有名字的就是右值 將亡值或純右值 舉個例子,int a b c,a 就是左值,其有變數名為a,通過 a可以獲取該變數的位址 表示式b c 函式int fun...