表達式
1. 基礎
c++ 定義了一元和二元運算符
- 一元:
&, *
:取地址和解引用 - 二元:
==, *
相等和乘法 - 三元:作用與三個對象的
1.1 組合運算符和運算對象
-
優先級
-
結合律
-
運算對象轉換
-
重載運算符
- IO庫的>>和<<運算符和類對象的運算
-
左右值
- 左值賦值,用作對象的身份
- 右運算,用對象的值
- 有時候左值也做運算
- 取地址符返回指向該對象的指針這個指針是右值
- 內置解引用,下標的求值結果都是左值
例如
decltype 左右值有所不同,如果是左值得到引用類型
int *p = &a; decltype(*p) <-是一個int& 型,因爲解引用是左值 decltype(&p) <-是一個 int**類型,也就是指向指針的指針
1.2 優先級與結合律
普通模式下的運算符就不說了
在優先級相同的情況下,但是沒說按照什麼順序求值。大多情況都不明確
括號無視優先級與結合律
-
同優先級的沒定義順序的運算來說,是無法判斷的
cout << i << " " << ++i <<endl; 我們是不知道i是否先加1的。所以程序是錯誤的
-
規定順序的有
&&
先求左側運算值||
、?:
,,
都是有順序的
-
求值順序、優先級、結合律
如:
f()+g()*h()+j()
中- 優先級決定,
g()
的返回值和h()
的返回值相乘 - 結合律決定,
f()
的返回值先與g()
和h()
的乘積相加,所得結果與j()
的返回值相加 - 對於函數的調用順序是沒規定的
- 優先級決定,
-
所以在做這類運算時,函數之間必須要沒有關係
-
[外鏈圖片轉存中…(img-sAiFlpgO-1583040490611)]
bool不是0就是1
- 浮點型不能取餘
- 取餘的定義:
m%n
如果不等於0,則它的符號和m相同 - m%(-n)等於m%n, (-m)%n 等於-(m%n)。
- 取餘的定義:
1.3 邏輯運算符
關係運算符作用於算術或指針類型,邏輯-》任意轉bool
[外鏈圖片轉存中…(img-EXsUvL2Z-1583040490616)]
- 短路求值
||
:對於左運算對象爲假才進行右運算&&
:對於左運算對象爲真才進行右運算
1.4 關係運算符
左結合律
if (i < j < k) //像這種 它屬於 先執行 i<j 得到 一個布爾值 再用這個布爾值跟k比較,也就是如果k>1就一定是真所以儘量不要這樣用
1.5 賦值運算符
左運算對象必須是一個可修改的左值, 初始化不是賦值
- c++11 允許用
{}
括起來賦值
int k;
k = {3.14}; //錯誤: 窄化轉換 如果初始化對象是內置類型,初始化列表,嚴格不能損失精度,所以會報錯
vector<int> v;
vi = {0, 1, 2, 3, 4, 5}; //現在v 有6個元素了
-
右結合律
- i = j = 0;
-
未定義左右運算順序
1.6 自加自減
-
解引用
cout << *pd++ << endl; //先輸出當前值在後移一位 cout << *++pd << endl; //先移一位在後輸出當前值
1.7 成員訪問運算符
點和箭頭運算符,可訪問成員;
string s1 = "a string ", *p = &s1;
auto n = s1.size();
n = (*p).size();
n = p->size(); //三者意義相同
- 解引用優先級低於引用
1.8 條件運算符
?:
允許我們把if-else 簡短嵌入
- 都是左值運算
fi = (gr > 90) ? "sss"
: (gr < 60) ? "fail" : "pass";
- 嵌入時要寫括號優先級很低
1.9 位運算
把對象當二進制進行運算
- 左結合律
符 | 功能 |
---|---|
~ | 取反 |
>> << |
左右移 |
& | 位與 |
^ | 位異或 |
| | 位或 |
- 若運算小整數將提升位較大的整數一般只轉int
- 若對象爲負值,那如何處理符號位依賴與機器,是未定義的行爲
- 移動超屆的會自動捨棄
unsigned char bit = 0233;
bit << 8 //會自動轉int
-
位取反
-
1和0交換
-
前面的位數都會補0
-
負數的反碼爲:除符號位外,原碼各位取反,反碼加1,得負數的反碼.
補碼爲反碼+1,計算機中正數是用原碼顯示,負數用補碼錶示
char bit = 1; 0~31 1 ~bit == -2
根據機器所得 : 得到的1~31 0 ,因符號位爲1所以是補碼,原碼就爲1 0~29 10
-
1.9.1 位與、位或、位異或運算符
都是兩個對象上的邏輯操作
-
現在的計算機都是32位的所以int可以移動32位
-
如果要超過就可以在前面轉型
1ULL << 34; //unsigned long long q &= ~(1<<27); //讓27位爲0
1.10 移位運算符
- 滿足左結合律
1.11 sizeof運算符
返回一條表達式或一個類型名字所佔的字節數
- 右結合,
size_t
類型
sizeof(type);
sizeof expr; //返回表達式結果類型的大小,不計算運算的值
sa_dat data, *p;
sizeof(sa_dat); //這個類型的空間大小
sizeof data; //同上
sizeof p; //指針的大小
sizeof *p; //指針所指空間的大小
- 對數組會得到數組得總大小
- 對string 和vector 只會返回類型固定大小不會計算總大小
sizeof
返回的是常量表達式,所以可以用作數組維度
1.12 逗號運算符
從左向右的求值順序
先求左側的表達式求值,然後將值丟棄,
,
號真正的結果只有右側的表達式
2. 類型轉換
面對浮點轉整型會損失精度
- 比int小的整數會轉int
- 在條件中非布爾會轉布爾
- 初始化右側轉左側
- 等等
2.1 算術轉換
一個算術類型轉另一個算術類型
- 有浮點時整數會轉浮點
- char中(wchar16_t, wchar32_t) 會提升到int,unsigned int。。。。選擇最小的,能容納原來所有可能值的
short + char ; //都轉int
- 有無符號的結果依賴機器,根據所佔空間大小進行升級和轉換
2.2 隱式轉換
- 數組用decltype 關鍵字,或取地址,sizeof 及typeid 等時不會發生轉換
- 指針只有 0, nullptr 可以和 非常量的
void*
和const void*
- 繼承類之間的指針轉換
2.3 顯示轉換
強制轉換
-
一個命名的強制轉換形式
- 一旦類型不符合,會產生未定義的後果
cast-name<type>(expression); type目標類型,expression 要轉換的值 cast: static_cast, dynamic_cast, const_cast, reinterpret_cast 中的一種 double sl = static_cast<double>(j)/i; int a[10] = {0, 1, 2, 3, 4}; void* S = &a; //非常量對象存入void* int *dp = static_cast<int*>(S); //將void*轉換回到初始的指針類型
-
const_cast
- 去掉const性質
const char *pc; const char s[] = "123"; const char *cp = s; char *p = const_cast<char*>(pc);//但p是未定義的行爲去掉const操作 char *p1 static_cast<char*>(cp); //錯誤static_cast 不能改const性質 static_cast<string>(cp); //正確,把字面值轉換了,並不會更改掉cp const_cast<string>(cp); //錯誤,const_cast只改變常量屬性 char *p1 const_cast<char*>(cp); //正確 可以通過p1改const性質 p1可以修改常量cp的值,但cp自己不行
-
reinterpret_cast
:爲運算對象的位模式提供較低層次上的重新解釋
int *ip; char *pc = reinterpret_cast<char*>(ip); 與舊式的 char *pc = (char*) ip; 一樣
參考文獻:c++ prime 第五版