《C++ primer》英文第五版閱讀筆記(十三)——表達式基本知識

Chapter4,Section4.1——Fundamentals


基本原理
對於如何計算表達式的值有許多基本的概念,在這裏先簡短進行介紹,後面會進行詳細的介紹。


(一)基本概念
操作符有一元的,二元的,還有三元的。
一元的操作符只有一個操作數(例如取地址符&,解引用符*等)。
二元的操作符有兩個操作數(例如等於符號==,乘法符號*等)。
三元的操作符有三個操作數和一個操作符。函數調用對於操作數的個數沒有限制。
有些操作符,比如*,既有一元的含義,又有二元的含義。要具體看它代表的含義。
但是它的使用是獨立的,可以將它當做兩個不同的操作符來看待。


操作符和操作數的組合
要想理解含有多個操作符的表達式,要理解操作符之間的優先級和結合性,有時候表達式的值也可能取決於計算操作數的順序。


操作數的類型轉換
當計算一個表達式時,操作數可能要經常進行類型轉換。
我們可以把一個整型轉換成浮點型,反之亦然,但是我們不能把一個指針類型轉換爲浮點型。
通常小整型類型(比如short,char,bool等)會被提升爲更大一點的整型,最典型的是int。


重載操作符
在C++中定義了當操作符被用在內置類型和複合類型中的具體的含義。對於在類類型中使用的操作數,我們也可以定義它們中大多數的含義。因爲這種定義方式可以對已經存在的操作符提供可選擇的含義,因此把這種方式叫做“重載操作符”。IO庫裏面的<<和>>操作符,以及我們在strings,vectors,iterators中使用的操作符都是重載操作符。
當我們使用一個重載操作符時,操作符的含義——包括它的操作數的類型和計算的結果都取決於這個操作符是如何進行定義的。然而,操作數的數量、操作符的優先級和結合性是不能被更改的。


左值和右值
C++中的每個表達式,不是一個左值,就是一個右值。這些名字是從C中繼承過來的,並且最初有一個簡單的助記方法:左值能放在賦值語句的左邊,但是右值不能。
在C++裏面,左值和右值的區別很簡單。一個左值產生一個對象或函數。然而有些左值(比如一個const對象),可能不能作爲賦值當中的左操作數。還有一些表達式產生了對象,但是將它們作爲右值返回,而不是左值。概括地說,當我們將一個對象作爲右值使用時,我們使用的是這個對象的值(它的內容);當我們將一個對象作爲左值來使用時,我們使用的是這個對象實體(它在內存中的位置)。
操作符在關於到底是需要左值操作數還是右值操作數是不同的,在它們到底是返回左值還是右值也是不同的。但是重要的一點是(有一個例外後面介紹),當需要右值時,可以使


用左值;但是當左值需要時,不能使用右值。當用左值替代右值時,使用的是左值對象的值。我們已經使用了許多包含左值的操作符。
1.賦值運算需要一個非const的左值作爲它的左操作數,並且將它的左操作數作爲一個左值。
2.地址符需要一個左值操作數並且把它的操作數返回一個指針,這個指針被當做右值。
3.內置的解引用和下標操作符,iterator解引用和vector與string的下標操作符都產生左值。
4.內置的和iterator的自增和自減操作符需要的是左值操作數,並且前綴形式的自增和自減操作符也產生左值。


當使用decltype時,左值和右值也是不同的。當我們在表達式中使用decltype時(而不是在變量中),如果表達式產生一個左值的話,結果是一個引用類型。
例如:假設p是一個int*類型的指針。由於解引用產生一個左值,decltype(*p)的類型是int&。由於地址符產生的是右值,decltype(&p)的類型是int**,也就是指向int類型指針的指針。


(二)優先級和結合性
含有兩個或者更多操作符的表達式是一個複合類型的表達式。計算複合類型表達式的值需要對操作符的操作數進行組織。優先級和結合性決定了操作數是如何被組合的。也就是說,它們決定了表達式中的哪一部分是每一個操作符的操作數。編程者可以不管這些規則通過在複合表達式中使用括號來限制一個特定的組合。
通常,一個表達式的值取決於子表達式是如何進行組合的。優先級高的操作符的操作數比優先級低的操作符的操作數組合的更緊密。結合性決定了相同優先級的操作符的操作數是


如何進行組合的。例如,乘法和除法運算符的優先級相同,但是它們都比加法運算符的優先級高。所以乘法和除法的操作數比加法和減法的操作數先進行組合。算術操作符是左結合的,這就


意味着相同優先級的操作符從左向右進行組合。


括號可以不管優先級和結合性
通過使用括號,可以不用去管正常的組合。含有括號的表達式在計算時,把每一個括號子表達式當成一個單元,其餘的部分使用正常的優先級規則。

IO操作符是左結合的,所以我們可以在一行中寫許多IO操作符。


(三)計算的順序

優先級決定了操作數是怎樣進行組合的,但是並不能代表操作數被計算的順序。在大多數情況下,計算的順序是未知的。


在表達式中如果出現了未指明計算順序的操作符,其結果是未知的。

例如:int i=0;

   cout << i << " " << ++i << endl; //undefined

這段程序的結果是未知的。編譯器可能在計算i之前計算++i,輸出會是1 1。編譯器也可能先計算i,再計算++i,結果是0 1。編譯器也可能完全做一些別的事。由於這個表達式的具體行爲是未知的,無論編譯器生成什麼樣的代碼這段程序都是有錯的。

有四個操作符能夠保證操作數的計算順序,分別是&&,||,?:,與逗號(,)。

操作數被計算的順序是獨立於優先級和結合性的。
例如:表達式f()+g()*h()+j()
優先級能夠保證g()和h()進行乘法運算。
結合性保證了f()會和g()*h()的結果相加,相加之後的結果會和j()進行相加。
但是無法確定這些函數的調用順序,哪個先被調用,哪個後被調用。

如果這四個函數是相互獨立的函數,並不會影響一個相同的對象或進行IO操作,那個它們被調用的順序就是未知的。如果它們作用於相同的對象,那麼這個表達式就會出錯,其結果是未知的。


當使用複合表達式時,記住下面的兩個規則:

1.括號會改變運算的邏輯。

2.當改變了一個操作數的值時,不要在同一個表達式裏面再使用那個操作數。

第二條規則有一個例外:當改變操作數的子表達式它自己本身也是另一個表達式的操作數時。(例如*++iter)


發佈了50 篇原創文章 · 獲贊 7 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章