C++ Primer 學習筆記(第四章:表達式)

##C++ Primer 學習筆記(第四章:表達式)express


[TOC]數組


###4.1 基礎安全

  1. 左值和右值:
    當一個對象被用做右值的時候,用的是對象的值(內容);當對象被用做左值的時候,用的是對象的身份(在內存中的位置)。 一個重要的原則是在須要右值的地方能夠用左值來代替,可是不能把右值當成左值(也就是位置)使用。當一個左值被當成右值使用時,實際使用的是它的內容(值)。函數

  2. 使用decltype的時候,若是表達式的求值結果是左值,decltype做用於該表達式獲得一個引用類型。 p的類型是int*,而*p會生成左值,多以decltype(*p)會獲得引用int &。 由於取地址會生成右值,因此decltype(&p)的結果是int**post

  3. 對於那些沒有指定執行順序的運算符來講,若是表達式指向並修改了同一個對象(或者執行了IO任務),將會引起錯誤併產生未定義的行爲。例如:學習

int i = 0;
cout << i << " " << ++i << endl;

有4種運算符明確規定了運算對象的求值順序:邏輯與&&(先求左側,左側爲真再求右側)、邏輯或||、條件運算符?:、逗號運算符,ui


###4.2 算術運算符.net

  1. 整數相除的結果仍是整數,若是有小數部分直接棄除。 結果爲負的商統一貫0取整,即直接切除小數部分。指針

  2. 參與取模運算%的運算對象必須是整數。 表達式(m/n)*n+m%n的結果與m相同,即m%n若是不等於0,則它的符號應該和m相同。code

-21 % -8; // -5		-21 / -8; //2
 21 % -5; //1			21/ -5; //-4

###4.3 邏輯關係和運算符

  1. 短路求值: 邏輯與(&&):當且僅當左側運算爲真時纔對右側進行求值; 邏輯或(||):當且僅當左側運算爲假時纔對右側進行求值。

  2. 進行比較運算時除非比較的對象時布爾類型,不然不要使用布爾字面值true

if (val == true) {/*...*/} //只有當val等於1時條件才爲真

###4.4 賦值運算符

  1. 若是賦值運算符的左右兩個運算對象類型不一樣,則右側運算對象將轉換成左側運算對象的類型。
int k = 3.14159;
  1. C++11容許使用花括號括起來的初始值列表做爲賦值語句右側對象。
k = {3.14};
vector<int> vi = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  1. 把賦值語句放到條件中的用處:(賦值語句的優先級比較低)
while ((i = get_value()) != 42){
	//其餘處理
}

###4.5 遞增和遞減運算符

  1. 關於前置和後置++運算符。 除非必須,不然不用遞增遞減運算符的後置版本。(由於對於複雜的迭代器類型,後置須要將原始值存儲下來,可能涉及到額外的消耗。)

  2. 混用解引用和遞增運算符。

auto pbeg = v.begin();
while (pbeg != v.end() && *pbeg >= 0)
	 cout << *pbeg++ << endl;

後置++的優先級高於解引用運算符,表示獲得初始值的副本再遞增。 這種*pbeg++的寫法更簡潔。


###4.6 成員訪問運算符

  1. 表達式ptr->mem等價於(*ptr).mem

  2. 解引用運算符*優先級低於點運算符,因此執行解引用運算的子表達式兩端必須加上括號。(*p).size()


###4.7 條件運算符(?:

  1. cond ? expr1 : expr2,條件運算符只對expr1expr2中的一個求值。expr1expr2的類型應該相同或者能轉換成同一種類型。

  2. 條件運算符的優先級很是低,所以當一條長表達式中嵌套了條件運算子表達式時,一般須要在它兩端加上括號,尤爲是在輸出表達式中,不然將產生意想不到的效果。

cout << ((grade < 60) ? "fail" : "pass") << endl;

###4.8 位運算符

  1. 位運算符做用於整數類型的運算對象,並把運算對象當作是二進制位的集合。

  2. 若是運算對象是「小整型」,它的值會被自動提高成較大的整數類型。

  3. 移位運算符>> <<右側的運算對象必定不能爲負,並且值必須嚴格小於結果的位數,不然將產生未定義的行爲。

  4. 利用位運算符能夠檢測位:

unsigned long quiz1 = 0;//32位
quiz1 |= 1 UL << 27;//表示將第27位置成1
quiz1 &= ~(1 UL << 27);//表示將第27位置成0
bool status = quiz1 & (1 UL << 27);//檢測第27位

###4.9 sizeof運算符

  1. sizeof運算符返回一條表達式或一個類型名字所佔的字節數,其所得的值是一個size_t類型的常量表達式。當返回一個表達式的值時,sizeof expr並不實際計算其運算對象的值。
int i = 0;
cout << sizeof( i++ ) << endl;//4
cout << i <<endl;//0
  1. sizeof *p:由於sizeof不會實際求運算對象的值,因此即便p是一個無效的(即未初始化)指針也不會有什麼影響。在sizeof的運算對象中解引用一個無效指針仍然是一種安全的行爲,由於指針實際上並無被真正利用。

  2. sizeof Sales_data::revenue:C++11容許使用做用域運算符來獲取類成員的大小。sizeof運算符無須咱們提供一個具體的對象就能訪問到類成員,由於要想知道類成員的大小無須真正的獲取該成員。

  3. sizeof運算符的結果: char類型或類型爲char的表達式 引用類型獲得被引用對象所佔空間的大小 指針類型獲得指針自己所佔空間大小 對數組執行獲得整個數組所佔空間大小,等價於全部元素各執行一次,並將結果求和。(而不會把數組當成指針處理) 對string對象或vector對象執行只返回類型固定部分的大小,不會計算對象中的元素佔用了多少空間。

vector<int> v{0};
cout << sizeof v << endl;//12
  1. 返回數組元素的數量:
constexpr size_t sz = sizeof(ia) / sizeof(*ia);
int arr2[sz];//正確,sizeof返回一個常量表達式。

###4.10 逗號運算符

  1. 逗號運算符含有兩個運算對象,按照從左向右的順序依次求值,先對左側的表達式求值,而後將求值結果丟棄掉,逗號運算符真正的結果是右側表達式的值。
int i = 1, j = 100;
cout << (i++, j++) << endl;
  1. 逗號運算符的優先級在最末位。

###4.11 類型轉換

  1. 算術轉換:運算對象將轉換成最寬的類型;當表達式中既有浮點型也有整數類型時,整數值將轉換成浮點類型。

  2. 數組轉換成指針:在大多數用到數組的表達式中,數組自動轉換成轉向數組首元素的指針。當數組用做decltype關鍵字參數,或者做爲取地址符(&)、sizeoftypeid等運算符的運算對象時,上述轉換不會發生。

  3. 常量整數值0或者字面值nullptr能轉換成任意指針類型;指向任意很是量的指針能轉換成void*;指向任意對象的指針能轉換成const void*

  4. 轉換成常量:能將指向T的指針或引用分別轉換成指向const T的指針或引用。但相反的轉換不存在。

int i;
const int &j = i;
const int *p = &i;
int &r = j, *q = p;//錯誤,不容許const轉換成常量
  1. 類類型轉換:
string s, t = "a value";//字符串字面值轉換成string類型
while (cin >> s)//while的條件部分把cin轉換成bool值
  1. 強制類型轉換: cast-name<type>(expression):其中type表示轉換的目標類型,expression是要轉換的值。若是type是引用類型,則結果是左值。cast-name指定了執行的是那種轉換,包括static_castdynamic_castconst_castreinterpret_cast中的一種。

  2. static_cast:具備明肯定義的類型轉換,只要不包含底層const,均可以使用static_cast

double slope = static_cast<double>(j) / i;

當須要把一個較大的算術類型賦值給較小的類型時很是有用,此時意味着告訴編譯器不在意潛在的精度損失,而且不會產生警告。對於編譯器沒法自動執行的類型轉換也頗有用,例如能夠用來找回存在於void*指針中的值:(但應當確保轉換類型必定正確)

void *p = &d;//任何很是量對象的地址都能存入void*
double *dp = static_cast<double*>(p);
  1. const_cast:只能改變運算對象的底層const(常量指針或聲明引用的const),稱爲去掉const行爲;它只能改變常量屬性,不能改變常量類型。
const char *pc;
char *q1 = const_cast<char*>(pc);//正確,可是經過p寫值是未定義行爲
char *q2 = static_cast<char*>(pc);//錯誤,static_cast不能轉換掉const性質
string s1 = const_cast<string>(pc);//錯誤,const_cast只轉換屬性
string s2 = static_cast<string>(pc);//正確,字符串字面值轉換爲string類型

const_cast經常使用於有函數重載(6.4節)的上下文中。

const_cast的用法不是將一個const變量變成非const變量的。看一下標準就能夠知道,使用const_cast把一個本來是const的變量轉換爲非const,這是一個未定義行爲,是很是很是危險的舉動。 const_cast的目的,在於某些變量本來不是const的,但因爲某種特殊緣由,無心間被變成了const的,例如使用了一個const引用指向了一個原本不是const的對象。結果寫了一些代碼以後發現它實際上須要被修改。 這在平時的工做中不會遇到由於你能夠直接把const引用修改爲非const的,但C++中可能的狀況太多,尤爲考慮到不少複用的時候,有時仍是會出現本不應是const的對象被const引用了這種狀況。尤爲是使用模板,比較複雜的狀況。這纔是const_cast的意義所在。(摘自:求助const_cast的問題(CSDN)

  1. reinterpret_cast:爲運算對象的位模式提供較低層次上的從新解釋。
int *ip;
char *pc = reinterpret_cast<char*>(ip);//pc所指的真實對象是一個int而非字符,若是把pc當成普通的字符指針使用就可能在運行時發生錯誤,例如string str(pc)

使用reinterpret_cast很是危險。使用一個int的地址初始化pc,顯示地聲稱這種轉換合法,編譯器不會報錯,但繼續使用將引起糟糕的後果。

  1. dynamic_cast支持運行時類型識別,19.2節介紹。

  2. 強烈建議避免使用強制類型轉換,除了重載函數上下文使用const_cast(6.4節)無可厚非,其餘狀況都應反覆斟酌。

  3. 舊式強制類型轉換:

type (expr);//函數形式
(type) expr;//C語言風格

它們與上述三種強制轉換有類似行爲。

相關文章
相關標籤/搜索