C 左值引用和右值引用

2021-10-10 12:48:21 字數 3347 閱讀 8939

右值引用

引用為物件起了另外乙個名字, 引用型別引用另外一種型別,通過將宣告符寫成&d的形式來定義引用型別,其中d是宣告的變數名;

例子:

int ival =

1024

;int

&refval = ival;

//定義了ival的引用,refval指向ival,是ival的另乙個名字,所以說,引用即是別名

int&refval2;

// 報錯;

引用型別必須被初始化

一般在初始化變數時,初始值會被拷貝到新建的物件中去,然而定義引用的時候,程式會把引用和它的初始值繫結在一起,而不是將初始值拷貝給引用,一旦初始化完成,引用將和它的初始值物件一直繫結在一起,所以無法令引用重新繫結到另外乙個物件,所以引用必須初始化;這也就是為什麼上面示例**中第三行會報錯的原因,正是因為其沒有進行初始化;

不能定義引用的引用

由上面的示例**的第二行我們可以明白,引用即是別名,引用並不是物件,它只是為乙個已經存在的物件所起的另乙個名字,也就是說應用型別並不開闢任何新的記憶體空間。

當你定義了乙個引用之後,你對該引用的所有操作,其實都是在與之繫結的物件上進行的,正因為引用本身不是乙個物件,所以不能定義引用的引用。

所有的引用型別都要和與之繫結的物件嚴格匹配

在這裡有兩種例外情況,我們將在下文進行闡述。

int i =42;

const

int&r1 = i;

// 定義乙個常量引用r1,使用常量引用r1繫結非常量物件i,在這裡常量引用r1不能被用作修改其所繫結物件i的值

const

int&r2 =42;

// 正確,因為r2是乙個常量引用,所以其可以繫結字面值

const

int&r3 = r1 *2;

// 正確,因為r3是常量引用,所以其可以用來繫結乙個表示式的結果;

int&r4 = r1 *2;

// 報錯,錯誤1 :不允許讓乙個非常量引用r4指向乙個常量引用,假如這種行為是成立的,那麼也就意味著可以通過引用r4來修改r1的值,這顯然是矛盾的。 錯誤2 :我們上文提到,不能定義引用的引用,這裡的r1和r4均是引用。

總結來說就是***不能讓非常量引用指向乙個常量物件,但允許常量引用繫結非常量的物件,字面值,表示式***

值得注意的是:常量引用僅對引用可參與的操作做出了限制,對於引用的物件本身是不是乙個常量未做限制,其引用的物件也可能是個非常量值,允許通過其他的途徑來改變它的值;

允許在一條語句中定義多個引用,其中每個引用識別符號都必須用&開頭

非常量引用只能繫結在物件上,而不能與字面值或某個表示式的計算結果繫結在一起

我們通過乙個簡單的例子來理解為什麼

例子:

double dval =

3.14

;const

int&ri = dval;

此處的ri被定義為引用int型別的物件,所以對ri的操作應該是整數運算,但是dval卻是乙個雙精度浮點數,因此為了確保讓ri繫結乙個整數,編譯器將上述**變成了如下形式:

const

int temp = dval;

// 由雙精度浮點數生成乙個臨時的整形常量

const

int&ri = temp;

// 讓ri繫結這個常量

在這種情況下,ri繫結了乙個臨時量物件(當編譯器需要乙個空間來暫存表示式的求值結果時臨時建立的未命名物件,c++程式設計師們常常把臨時量物件簡稱為臨時量)

接下來我們進入這個例子真正的主題,假如當ri不是常量的時候,也就是說我們違反了引用型別和與之繫結的型別物件必須嚴格匹配的原則,此時會產生什麼樣的後果呢?

如果ri不是常量,那麼就允許對ri賦值,這樣就會改變ri所引用物件的值,注意:此時繫結的物件是乙個臨時量而非dval,程式設計師既然想讓ri引用dval,就肯定是想通過ri改變dval的值,否則幹什麼要給ri賦值呢?如此看來,既然大家基本上不會想著把引用繫結到臨時量上,那麼c++語言也就把這種行為歸位非法了即除了特殊情況所有的引用型別都要和與之繫結的物件嚴格匹配,而且常量引用只能繫結在物件上。

int

&refval4 =10;

// 報錯,引用型別的初始值必須是乙個物件而不能是字面值

double i =

3.14

;int

&refval5 = i;

//報錯,引用型別必須和與之繫結的型別嚴格匹配,這裡引用的型別是int,而與之繫結的物件型別是double;

在闡明右值引用之前我們需要先明確一下什麼是左值,什麼是右值:

左值和右值是表示式的屬性,一般而言,乙個左值表示式表示的是乙個物件的身份,而乙個右值表示式表示的是物件的值。

所謂右值引用就是必須繫結到右值的引用,我們通過&&而不是&來獲得右值引用,右值引用有乙個重要的性質:只能繫結到乙個將要銷毀的物件,因此,我們可以自由地將乙個右值引用的資源移動到另乙個物件中。

我們不能將左值引用繫結到要求轉換的表示式,字面常量或是返回右值的表示式上,右值引用有著完全相反的繫結特性:我們可以將乙個右值引用繫結到這類表示式上,但不能將乙個右值引用直接繫結到乙個左值上,值得特別注意的是,因為變數可以看作只有乙個運算物件而沒有運算子的表示式,類似其他的任何表示式,變數表示式也有左值右值屬性,變數表示式都是左值,,帶來的結果就是我們不能把乙個右值引用繫結到乙個右值引用型別的變數上。

例子:

int

&&rr1 =42;

// 正確

int&&rr2 = rr1;

// 錯誤;rr1是乙個右值引用型別的變數

返回左值引用的函式,連同賦值,下標,解引用和前置遞增前置遞減運算子都是返回左值的表示式的例子,我們可以把乙個左值引用繫結到這類表示式的結果上。

返回非引用型別的函式,連同算數,關係,位運算以及後置遞增後置遞減運算子都生成右值。我們可以把乙個const的左值引用或者右值引用繫結到這類表示式上。

左值有持久的狀態,而右值要麼是字面值常量,要麼是在表示式求值過程中建立的臨時物件。

由於右值引用只能繫結到臨時物件,我們得知:

通過呼叫名為move(標頭檔案utility)的新標準庫函式來獲得繫結到左值上的右值引用。

move告訴編譯器:我們有乙個左值,但我們希望像乙個右值一樣處理它。呼叫move就意味著承諾:除了對該左值賦值或銷毀它外,我們將不在使用它。我們可以銷毀乙個移後源物件,也可以賦予它新值,但不能使用乙個移後源物件的值,我們不能對移後源物件的值做任何假設。

我們直接呼叫std::move而不是提供using宣告後使用move;

int

&&rr3 = std::

move

(rr1)

;

C 左值和右值,左值引用和右值引用

c 對於左值和右值沒有標準定義,但是有乙個被廣泛認同的說法 可見立即數,函式返回的值等都是右值 而非匿名物件 包括變數 函式返回的引用,const物件等都是左值。從本質上理解,建立和銷毀由編譯器幕後控制,程式設計師只能確保在本行 有效的,就是右值 包括立即數 而使用者建立的,通過作用域規則可知其生存...

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

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

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

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