++a是先自增,然後返回增加後的變數a,a是變數,本來就可以做左值,所以(a++)++正確;a++是後自增——在運算式裡值還是原來的值——返回的是一個等於a+1的臨時變數,臨時變數是有const屬性的,不可以更改,所以(a++)++錯誤。
左值和右值
左值(left value):出現在賦值運算式的左邊的運算式,其作用為存放資料空間,且存放是允許的。縮寫:lvalue
右值(right value):只能出現在賦值運算式的右邊。縮寫:rvalue
左值也可以作為右值運算式,變數可以是左值,也可以為右值,但常量只能是右值。
a=b // b是變數,在這裡是左值,也可以作為右值
a=8 // 8是常量,只能作為右值
(a=4)=28 // C++文法規定:a=4為左值,28為右值
操作符的優先順序與結合性
c = a + b // OK
c = a + + b // Error
c = a + + + b // OK a ++ + b
c = a + + + + b // Error a ++ ++ b
c = a + + + + + b // Error a ++ ++ + b
操作符自左向右取儘可能多的符號組成運算子,有些操作符的結合性是自右向左的,操作符的其結合性以下表為準。class Couter{
private:
int number;
public:
Couter& operator++(int);
..............
}; 對於首碼運算式:
Couter& Couter::operator++()//返回引用
{
number++;
return *this;
}
這樣沒錯,但它的功能為<對a自加後,之作用在a自身>,
要實現尾碼式,這必須用
Couter Couter::operator++(int)//尾碼式,不返回引用
{
Couter temp;
temp=*this;
number++;
return temp;
}
這樣才能實現先取出a的值進行操作,繞後再對a自加,但同時帶來的
弊端為a自加後,重載算符函數的傳回值作用不到a自身,所以不可以
複合使用.即使編譯可以通過,但結果肯定不對!
1. C++左值與右值概念
變數和文字常量都有儲存區,並且有相關的類型,區別在於變數是可定址的;
對於每個變數,都有2個值與其相關聯:
1>資料值,儲存在某個記憶體位址中,也稱右值(rvalue),右值是被讀取的值(read value),文字常量和變數都可被用於右值。
2>地址值,即儲存資料值的那塊記憶體位址,也稱左值(lvalue),文字常量不能被用作左值。
2 . C++左值與右值的相關問題
給運算式加上括弧: ++a--
結果 ++(a--)
這個運算式是非法的,因為前增量操作要求一個可修改的左值,而 "a--" 不是左值(即右值)
3 . C++左值與右值中前增量和後增量的區別
早期的c語言教材,for迴圈語句通常寫成:
for(int i=0;i<10;i++)
而現在多為:
for(int i=0;i<10;++i)
兩者有區別嗎?
a++ 即是返回 a的值,然後變數 a 加 1,返回需要產生一個臨時變數類似於
- {
- int temp = a;
- aa=a+1;
- return temp; //返回右值
- }
++a 則為:
- {
- aa=a+1;
- return &a; //返回左值
- }
顯然,前增量不需要中間變數,效率更高。
C++左值與右值的含義與誤區
術語 “L-Values” 和 “R-Values” 是很容易被搞混的,因為它們的曆史淵源也是混淆。他們最初起源是編譯器的設計者,從字面上來理解就是運算式左邊的值和運算式右邊的值。它們的含義一直在演 化而名字卻沒變,現在已經“名”不副“實”了。雖然還是稱為left-value 和right-value,但是他們的含義已經大大不同了。
C++ 03 標準上是這樣寫的: “每一個運算式要麼是一個 lvalue,要麼就是一個 rvalue。”
記住,lvalue和rvalue是針對錶達式而言的。
lvalue 是指那些單一運算式結束之後依然存在的持久對象。例如: obj,*ptr, prt[index], ++x 都是 lvalue。
rvalue 是指那些運算式結束時(在分號處)就不複存在了的臨時對象。例如:1729 , x + y , std::string("meow") , 和 x++ 都是 rvalue。
++x 和 x++ 的區別的語義上的區別: 當寫 int i = 10 ; 時, i 是一個 lvalue,它實際代表一個記憶體裡的地址,是持久的。 運算式 ++x 也是一個 lvalue,它修改了 x 的值,但還是代表原來那個持久對象。但是,運算式 i++ 卻是一個 rvalue,它只是拷貝一份i的初值,再修改i的值,最後返回那份臨時的拷貝,那份拷貝是臨時對象。 ++i 和 i++ 都遞增i,但 ++i 返回i本身,而 i++ 返回臨時拷貝。這就是為什麼 ++i 之所以是一個 lvalue,而 i++
是一個 rvalue。
lvalue 與 rvalue 之分不在於運算式做了什麼,而在於運算式代表了什麼(持久對象或臨時產物)。 判斷一個運算式是不是 lvalue 的直接方法就是“能不能對錶達式取址?”,如果能夠,那就是一個 lvalue;如果不能,那就是一個 rvalue。
什麼情況下返回指標?什麼情況下返回引用?指標和引用的效果一樣嗎?這裡不僅涉及到引用的實現,也涉及到左值與右值的概念。因為傳回值性質的不同決定了引用與指標必定不是相同的。相信你讀過我寫的這篇文章之後,會有一個比較清醒的認識。左值(lvalue)和右值(rvalue)最先來源於C語言。最先在C語言中表示位於賦值運算子兩側的兩個值,左邊的就叫左值,右邊的就叫右值。
比如:
int ii = 5; //ii是左值,5是右值
int jj = ii; //jj是左值,ii是右值
上面表明,左值肯定可以作為右值使用,但反之則不然。左值和右值的最早區別就在於能否改變。左值是可以變的,右值不能變,但是這一點在C++中已經不再成立。在現代C++中,現在左值和右值基本上已經失去它們原本所具有的意義,對於左值運算式,通過具體名字(variant name)和引用(reference)來指定一個對象。非左值就是右值。我們來下一個定義:
左值表示程式中必須有一個特定的名字引用到這個值。
右值表示程式中沒有一個特定的名字引用到這個值。
這跟它們是否可以改變,是否在棧或堆(stack or heap)中毫無關係。1.左值
在下面的代碼中:
int ii = 5;
const int jj = ii;
int a[5];
a[0] = 100;
*(a+3) = 200;
const int& max(const int& a, const int& b) //call by reference
{
return a > b ? a : b;
}
int& fun(int& a) //call by reference
{
a += 5;
return a;
}
ii,jj,a[0],*(a+3)這些值,還有函數max的傳回值比如max(ii, jj),函數fun的傳回值fun(ii)都是左值。因為它們都是被特定的名字所引用的值。
ii,jj,a[0],*(a+3),以及引用了max和fun的傳回值的變數就是它們的名字。註:有人會問max(8, 9)到底是左值還是右值,C++標準規定常量引用
(reference to const)可以引用到右值,所以max(8, 9)似乎應該是右值。但根據我們對左值的定義,說它是左值也對。不管它是左值,還是右值,我們都不能試圖去改變它。為了與前面的概念一致,姑且認為它是左值,不可改變的常量左值。左值有不能改變的,即被const所修飾的左值,比如上面的jj,max(ii, jj),沒有被const困住的左值當然是可以改變的。
比如下面的代碼都是成立的:
ii = 600;
a[0] = 700;
fun(ii) = 800; //OK!fun(8);//錯,必須傳遞變數來初始化引用,如果參數是const int&就可以。我們的眼睛沒有問題,fun(ii) = 800;完全正確,因為它是可以改變的左值。2.右值
沒有特定名字的值是右值。
看下面的代碼:
int fun1() //call by value
{
…
}
int* fun2() //call by value
{
…
}
函數fun1的傳回值fun1(),函數fun2的傳回值fun2()都是右值,它們的值都沒有特定的名字去引用。
也許有人會奇怪,fun2()也是右值?前面說的max(a,b)不是左值嗎?請看清楚,函數fun2的傳回值是pointer,pointer也是call by value,而函數max的返
回值是reference,reference是call by reference。
當然字面上的(literal)值,比如5,8.23,’a’等等也都是右值。右值最初出現的時候,一個最大的特徵就是不可改變。。但時代不同了,標準也變化了。
C++中有可以改變的右值。那就是使用者自訂的類(class)的建構函式產生的臨時對象。
至 於原因,我思考了一下,我想是這樣的:我們看類(class)的資料布置結構,會發現它的每一個資料成員都是有名字的,我想編譯器在編譯的過程中,都會生 成一個外部不所知的對這個臨時對象右值的名字引用。但當需要改變這個臨時對象的時候,這個名字就用上了(實際這時變成了左值,因為有了名字引用)。
比如:
class Point
{
public:
int x;
……//其他各種成員函數
};我們現在就可以改變右值,用到了匿名的引用名字。
Point().x = 6;//改變了右值最後說一下引用的機制。很多人都在爭論引用是怎麼實現的?如果你看看vc做出的反組譯碼代碼,你會發現在機器碼的層次,指標與引用的實現是相同。 但是,如果你以此斷定引用就是指標,那麼就錯了。的確,引用是靠指標實現的,但是二者有區別。我們討論引用的問題,一定要在c++的語言層面上討論,而不 能跑到彙編代碼中去看。在c++中,引用就是個別名,本身不會另外分配空間。但是在彙編層次,你會發現“引用”有自己的空間。別忘了,你已經離開了c++的範圍,這樣的討論失去了意義。總之,我們應該明白,c++中的引用就是個別名,具體的實現方法也要取決於編譯系統和最佳化方案。