左值、右值、左值引用、右值引用
1. 左值
左值(lvalue,left value),顧名思義就是賦值符號左邊的值,可以取地址。準確來說,左值是表達式(不一定是賦值表達式)后依然存在的持久對象。
可以將左值看作是一個關聯了名稱的內存位置,允許程序的其他部分來訪問它。在這里,我們將 "名稱" 解釋為任何可用于訪問內存位置的表達式。所以,如果 arr 是一個數組,那么 arr[1] 和 *(arr+1) 都將被視為相同內存位置的“名稱”。
2. 右值
右值(rvalue,right value),右邊的值,是指表達式結束后就不再存在的臨時對象,不能取地址。
純右值(prvalue,pure rvalue),純粹的右值,要么是純粹的字面量,例如10, true;要么是求值結果相當于字面量或匿名臨時對象,例如1+2。非引用返回的臨時變量、運算表達式產生的臨時變量、原始字面量、Lambda表達式都屬于純右值。
小結:相對而言,右值則是一個臨時值,它不能被程序的其他部分訪問。為了說明這些概念,請看以下程序段:
int square(int a){ return a * a;}int main(){ int x = 0; // 1 x = 12; // 2 cout << x << endl; // 3 x = square(5); // 4 cout << x << endl; // 5 return 0;}在該程序中,x 是一個左值,這是因為 x 代表一個內存位置,它可以被程序的其他部分訪問,例如上面注釋的第 2、3、4 和 5 行。
而表達式 square(5) 卻是一個右值,因為它代表了一個由編譯器創建的臨時內存位置,以保存由函數返回的值。該內存位置僅被訪問一次,也就是在第 4 行賦值語句的右側。在此之后,它就會立即被刪除,再也不能被訪問了。
對于包含右值的內存位置來說,其本質就是:它雖然沒有名稱,但是可以從程序的其他部分訪問到它。
?
3. 左值引用
左值引用的聲明是通過在某個類型后放置一個符號&來進行的。前文代碼中的int & y = x;便是一個左值引用。
需要注意的是,在定義左值引用時,=右邊的要求是一個可修改的左值。因此下面幾種左值引用都是錯誤的:
左值引用的用途:?
(1) 作為復雜名稱變量的別名
我們可以寫出類似如下的語句:
可以看到,我們用簡短的whichList代替了其原本復雜的名稱,這能夠簡化我們的代碼書寫。
(2) 用于rangeFor循環
設想我們希望通過rangeFor循環使一個vector對象所有值都增加1,下面的rangeFor循環是做不到的、
但我們可以通過使用引用達到這一目的
for (auto & x : arr) ++x;(3) 避免復制大的對象
假定有一個findMax函數,它返回一個vector中最大的元素。若給定vector存儲的是某些大的對象時,下述代碼中的x拷貝返回的最大值到x的內存中:
在大型的項目中這顯然會增大程序的開銷,這時我們可以通過引用來減小這類開銷
auto & x = findMax(vector);類似的,我們在處理函數返回值的時候也可以使用傳引用返回。但是要注意,當返回的是類中私有屬性時,傳回的引用會導致外界能夠對其修改。
(4) 參與函數中的參數傳遞
在C和C++的函數中,addSelf(int x)這類函數對直接傳入的參數進行修改并不會改變原有參數的值。而有時我們希望能夠實現類似swap(int a, int b)這類能夠修改原參數的函數時,我們可以通過1.傳入指針和2.傳入引用實現。
swap函數的實現是一個很好的例子
4. 右值引用
首先要提出一個新的概念: 將亡值(xvalue,expiring value),是C++11為了引入右值引用而提出的概念(因此在傳統 C++中,純右值和右值是同一個概念),也就是即將被銷毀、卻能夠被移動的值。
C++11 引入了右值引用的概念,以表示一個本應沒有名稱的臨時對象。右值引用的聲明與左值引用類似,但是它使用的是 2 個 & 符號(&&),以下代碼使用了右值引用打印了兩次 5 的平方:
int && rRef = square(5);cout << rRef << endl;cout << rRef << endl;有意思的是,聲明一個右值引用,給一個臨時內存位置分配一個名稱,這使得程序的其他部分訪問該內存位置成為了可能,并且可以將這個臨時位置變成一個左值。
右值引用不能約束到左值上,所以,以下代碼將無法編譯:
再來看以下初始化語句:
int && rRef1 = square(5);在初始化完成之后,這個包含值 square(5) 的內存位置有了一個名稱,即 rRef1,所以 rRef1 本身變成了一個左值。這意味著后面的這個初始化語句將不會編譯:
int && rRef2 = rRef1;究其原因,就是右側的 rRef1 不再是一個右值。綜上所述,臨時對象最多可以有一個左值引用指向它。如果函數有一個臨時對象的左值引用,則可以確認,程序的其他部分都不能訪問相同的對象。
右值引用會涉及到很多概念比如移動語義,完美轉發,會在今后的文章中聊
引用和指針的區別
我們知道,指針是在內存中存放地址的一種變量,cpu能夠直接通過而變量名訪問唯一對應的內存單元,且每個內存單元的地址都是唯一的。
而變量名和引用,都可以看做內存的一個標簽或是標識符,計算機通過是否符合標識符判斷是否為目標內存,而一個內存可以有多個標識符
?
?
總結
以上是生活随笔為你收集整理的左值、右值、左值引用、右值引用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: using(别名)和range base
- 下一篇: 移动语义、完美转发