* 兩個整數相除的結果是整數。如果商含有小數部分,將被截掉。
* %該操作符只能被應用在整數類型(char、short、int、long)。當兩個操作數都是正數時,結果爲正。但是,如果有一個(或兩個)操作數爲負,餘數的符號則取決於機器。因此,移植性無法保證。
* 算術異常:算術表達式的計算會導致不正確或未定義的值。例如:除零、溢出。
* 標準C++頭文件limits提供了與內置類型表示有關的信息,另外C++編譯系統也提供了標準C頭文件climits和cfloat,它們定義了提供類似處理的預處理宏。如何使用這些頭文件來防止溢出和下溢,參見[PLAUGER92]的第4章和第5章。
* 浮點數的算術符還涉及精度的問題:在計算機中,當它表示一個數值時,只有固定的數位可以使用。浮點數加法、乘法和減法的結果精度受到底層數據類型的固有精度的影響。
4.2 等於、關係和邏輯操作符
* 二元關係操作符(小於或等於操作符)的缺點①:左右操作數的計算順序在標準C和C++都是未定義的,因此計算過程必須是與順序無關的。
* 缺點②:要判斷ival、jval、kval是否各不相同。if ( ival != jval != kval )是錯的,需要寫成如下,if ( ival != jval && ival != kval && jval != kval )。
4.3 賦值操作符
* 賦值和初始化都使用=。一個對象只能被初始化一次,也就是在被定義的時候,但在程序中可以被賦值多次。
* 將不同類型的表達式賦值給一個對象時,右邊表達式的類型必須與左邊被賦值對象的類型完全匹配。編譯器會試着隱式地將右操作數的類型轉換成被賦值對象的類型,如果轉換是合法的,則編譯器會悄悄地進行(如果會引起精度損失,通常會給出一個警告)。如果不可能進行隱式的類型轉換,那麼賦值操作被標記爲編譯時錯誤。
* 賦值操作符的左操作數必須是左值——必須由一個相關聯的、可寫的地址值
4.4 遞增和遞減操作符
* 使用斷言#include <cassert>,assert(條件表達式)
4.5 條件操作符
* 語法格式:expr1 ? expr2 : expr3
4.6 sizeof操作符
* sizeof的作用是返回一個對象或類型名的字節長度
三種形式:①sizeof(type_name) ②sizeof(object) ③sizeof object
* 返回類型是size_t,這是一種與機器相關的typedef的定義。在<cstddef>中定義
* sizeof應用在數組上,返回的是數組的字節長度,而不是數組的長度
* sizeof在編譯時刻計算,因此被看做常量表達式
4.7 new和delete表達式
* 沒有語法能爲動態分配的數組的元素指定一個顯示的初始集合,如果是類的對象數組,如果定義了缺省構造函數,那麼每一個元素都會使用缺省構造函數初始化。
* 將同一個對象釋放一次以上是不合法的,會在運行時產生一個未定義的行爲
4.9 逗號運算符
* 從左向右計算,結果是最右邊的值
4.10 位操作符
* 如果一個對象被用作一組位或位域的離散集合,稱此對象爲位向量(bitvector)
* 位向量是用來記錄一組項目或條件的是/否信息的緊縮方法
* 編譯器中,類型聲明的限定修飾符,如const或volatile,有時就存儲在位向量中
* iostream庫用位向量表示格式狀態,例如:輸出的整數是以十進制、十六進制等
* 支持位向量的兩種方式:
①使用內置整值來表示位向量,典型使用unsigned int
②標準庫bitset類,支持位向量的類抽象
建議使用標準庫的類抽象
* 用整值數據類型作爲位向量時,類型可以是有符號的,也可以是無符號的,強烈建議使 用無符號類型。因爲大多數的位操作中,符號位的處理是未定義的,因此在不同的實現 中符號位的處理可能會不同。在一個實現下可行的程序,在另一個實現有可能會失敗。
* 按位非操作符(~):翻轉操作數的每一位,每個1被設置爲0,而每個0被設置爲1。
* 移位操作符(<<、>>):將其左邊操作數的位向左或向右移動某些位。操作數中移到外 面的位被丟棄。左移操作符(<<)從右邊開始用0補空位。如果操作數是無符號數,則是右移操作符(>>)從左邊開始插入0,否則的話,它或者插入符號位的拷貝,或者插入0,具體由實現定義。
* 按位與(&):如果兩個操作數都含1,則結果是1。否則是0。
* 按位異或(^):在每個位所在處,如果兩個操作數只有一個含有1,則結果該位爲1,否則爲0。
* 按位或(|):如果兩個操作數有一個或者兩個含有1,則結果爲1,否則爲0.
* 通過使用常數1移位,再進行位操作來設置或取消某個位的值。
4.11 bitset操作
* 要使用bitset類,必須包含相關的頭文件。#include <bitset>
* bitset三種聲明方式:
- 缺省定義中,只需簡單地指明位向量的長度。例如:bitset<32> bitvec;缺省情況,所有位都被初始化爲0。
- 傳遞一個無符號參數。例如:bitset<32> bitvec2(0xffff);
- 傳遞一個代表0和1的字符串。例如:string bitval(“1010”); bitset<32> bitvec4(bitval);
* 轉換爲字符串:bitvec3.to_string();
* 轉換爲unsigned long:to_ulong();
4.12 優先級
* 優先級順序:
- ::(全局域)、::(類域)、::(名字空間域)
- .(成員選擇)
- ->(成員選擇)
- [](下標)
- ()(函數調用)()(構造函數)
- ++(後置遞增)
- --(後置遞減)
- C++類型轉換
- ++/--(前置遞增、遞減)
- ~/!/-/+(按位非、邏輯非、一元減、一元加)
- *(解引用)
- &(取地址)
- ()(類型轉換)
- *、/、%、+、-、
- << 、>>
- 比較 <、<=、>、>=、==、!=
- &、^(異或)、|(按位或)、&&(邏輯與)、||(邏輯或)、
- ?:(條件運算符)、=(賦值)、複合賦值(*=、/=、…)
- throw
- ,(逗號運算符)
4.13 類型轉換
* C++並不是把不同類型的值加在一起,而是提供一組算術轉換,以便在執行算術運算之前,將兩個操作數轉換成共同的類型。
* 轉換規則:小類型總是被提升成大類型,以防止精度丟失。這些轉換由編譯器自動完成,無需程序員介入----稱爲隱式類型轉換
* double到int的轉換不支持舍入,需要編寫程序來實現。
4.13_1 隱式類型轉換
* C++定義了一組內置類型對象之間的標準轉換,在必要時它們被編譯器隱式地應用到對象上。隱式類型轉換髮生在下列這些典型的情況:
- 在混合類型的算術表達式中。這種情況下,最寬的數據類型成爲目標轉換類型—稱爲算術轉換
- 用一種類型的表達式賦值給另一種類型的對象,這種情況,目標轉換類型是被賦值對象的類型
- 把表達式傳遞給一個函數調用,表達式的類型與形式參數的類型不相同。這種情況,目標轉換類型是形式參數的類型
- 從一個函數返回一個表達式,表達式的類型與返回類型不相同。這種情況下,目標轉換類型是函數的返回類型
4.13_2 算術轉換
* 算術轉換保證了二元操作符(如加法或乘法)的兩個操作數被提升爲共同的類型,然後再用它表示結果的類型。
* 爲了防止精度丟失,如果必要的話,類型總是被提升爲較寬的類型
* 所有含有小於整型的有序類型的算術表達式。在計算之前,其類型都會轉換成整型。對於比int小的整型,包括:char、signed char、unsgined char、short和unsgined short,如果類型的所有可能的值都能包容在int內,它們就會被提升爲int型,否則它們將被提升爲unsgined int。
* long類型的一般轉換有一個例外:如果一個操作數是long類型,另一個是unsigned int類型,只有機器上的long類型足夠長以便能夠存放unsigned int的所有值時,unsigned int纔會轉換爲long,否則兩個操作數都被提升爲unsigned long。
4.13_3 顯示轉換
* 也被成爲強制類型轉換(cast)
* static_cast、dynamic_cast、const_cast、reinterpret_cast
* const_cast,去掉表達式的常量性(以及volatile對象的volatile性)
* static_cast,類似於C風格的強制轉換。無條件轉換、靜態類型轉換。編譯器隱式執行的任何類型轉換都可以由static_cast完成。用於:
- 基類和子類之間的轉換
- 基本數據類型轉換
- 把空指針轉換成目標類型的指針
- 把任何類型的表達式轉換成void類型
- static_cast不能去掉const、volatile屬性
* reinterpret_cast----轉換爲不同類型(不安全)時候使用。例如:
double dval = 0.123;int *pi = reinterpret_cast<int*>(&dval);
4.13_4 舊式強制類型轉換
* 只有當爲C語言或標準C++之前的編譯器編寫代碼的時候用這個語法
* 兩種形式:
- type(expr) // C++強制轉換符號
- (type)expr // C強制轉換符號
* 對舊式強制轉換符號的支持是爲了對“在標準C++之前寫的程序”保持向後兼容性,以及提供與C語言兼容的符號