今天是第四章節的總結,挺快的,因爲我翻了翻書,還有那麼多沒看,頓感焦急,故趕忙開始寫總結。昨天在微信上看見一篇文章,大意是談焦慮的壓力,所以我也在提醒自己,要在高質量的前提下重視效率,不能因爲時間不充足就忽略了質量。
有錯誤 請指正 謝謝
1. 引言:
C++提供了豐富的運算符作用於內置類型運算對象。對於自定義數據類型用重載運算符機制提供支持。
2.表達式:
一個或者多個運算對象構造,對表達式求值得到一個結果。其中字面值和常量是最常見的表達式,其結果就是其本身。當運算對象遇到運算符就可以組合成複雜的表達式。
3.基本概念:
1.一元和二元運算符的區分:運算對象的個數。
2.組合運算符和運算對象的依據:優先級和結合律。
3.運算對象的類型轉換
4.重載運算符作用於自定義數據類型。重載的過程是重新確定了運算對象的類型和返回類型,並不能改變優先級和結合律。
5.左值和右值:一個表達式不是左值就是右值。在C中,爲了方便記憶,通常如此說:出現在等號左邊的就是左值,出現在右邊的當然的就是右值。
但是在現在的C++中,形式變的很複雜。
簡單的歸納是:當一個對象被當作右值使用的時候,使用的是它的的內容(值),當作左值使用的時候,使用的是它的身份(可以理解爲在內存中的位置)。需要使用右值的地方可以使用左值。
運算符對運算對象有要求,目前常見的需要使用左值的運算符有:
解引用,取地址,等號,下標,前置自增自減。
decltype處理左值右值的區別:當其中的表示式是左值的時候,返回的是引用類型。
示例:
int *p;
decltype(*p) s; //s的類型是int&,在編譯器中s是必須要綁定初值的,因爲是引用。
decltype(&p) p; //p的類型是int**。
4.優先級和結合律:
1.複雜表達式:存在兩個或者兩個以上運算符。複雜表達式的求值依賴於優先級和結合律,但是括號會無視優先級。只有當優先級相同的時候纔會用到結合律。
2.優先級和結合律的影響:
2+1*5;//*的優先級大於+號。所以等價於1+(1*5);
-----------------------------------------------
int v1,v2;
cin>>v1>>v2; //有兩個運算符,但是一樣,所以優先級相同。根據它的左結合律,所以等價於:
((cin>>v1)>>v2);
5.求值順序:
優先級只是規定了運算符和運算對象的組合方式,但是並未規定了運算對象的求值順序,大多數情況下運算符是不規定對象的求值順序的。
示例:
int i=f()*g();
//*運算符並未規定求值順序,也就是說我們並不知道哪個函數會被先調用。
對於那些並沒有指定執行順序的運算符來說,當在同一個表達中修改了一個運算對象,並且在此表達式後面還會用到此對象,那麼會產生未定義行爲。
示例:
int i=1;
cout<<i<<++i<<endl;
//會產生未定義行爲。
我們知道他們的結合方式,左結合。但是表達式的執行過程是要修求出i和++i的值。那麼到底是先求出i然後求++i的,還是到過來。我們並不清楚,所以產生未定義行爲。
在我的vs2013 中,求值順序是從右到左的。
#include <iostream>
using namespace std;
int main(){
int i = 1;
cout << i << ++i << endl;
}
// output:22.
四個規定了求值順序的運算符:
&& || , ?: 這四個運算符是規定了求值順序的。比如我們已經知道的布爾邏輯(短路邏輯)
總而言之,運算對象的求值順序與優先級和結合律是無關的。一般求值順序是不影響表達式的最終結果。
tips:
1.當你拿不準優先級的時候請使用括號。
2.改變運算對象的表達式中不要在其他地方繼續使用此對象。但是存在一個例外
示例:
*++iter;
*iter++;
在這個例子中,改變運算對象的子表達式的值就是另一個子表達式的運算對象,不影響使用。有的書是如下解釋的,雖然++ 和* 的優先級一樣,但是右結合的,所以等價於:
*(iter++)
6.算術運算符:
優先級:一元+ -號>乘除>加減。
結合律:左結合
求值結果都是右值。
示例:
<span style="color:#330099;">bool b=true;
bool b2=-b;
//會發生什麼呢?</span>
這個地方會發生類轉換。最終的結果會是b2=1(true);
值環繞行爲:符號位由0變爲1.
示例:
#include <iostream>
using namespace std;
int main(){
short var = 32767;
var += 1;
cout << var<< endl;
}
//output: -32768.
因爲short 的取值範圍是 [-32768,32767]。所以加了會越界,故發生了值環繞。%運算符運算對象必須是整形。負商會直接切除0.
m=(m/n)*n+(m%n);
-m/n;m/(-n) 等價於:-(m/n);
m%(-n) 等價於:m%n;
(-m)%n等價於:-(m%n);
有 的是C++11 的規定,請不要奇怪。7.邏輯和關係運算符:
tips:除非比較的對象是布爾型,否則不要用布爾值進行比較。推薦使用簡寫。
示例:
if(var==1)
if(var==true)
//除非var是布爾值,否則不推薦寫成下面的形式。
8.賦值運算符:
左側運算對象必須爲非常量左值。
兩個運算對象類型不同時,右側運算對象會轉換成左側類型後,然後進行運算。
窄化轉換:C++11 規定的列表初始化行爲會報錯。
示例:
<span style="font-size:18px;">int i={3.14}
//窄化轉換,報錯。</span>
當左側對象是內置類型時,初始化列表最多只能有一個值,自定義類型不受限制。
結合律:左結合。
優先級:優先級較低,注意使用括號。
複合賦值運算符:
+= -= *= /=
不要侷限的認爲就這幾個,上面是常見的。
等價於:a=a op b;
區別:複合運算符的左側運算對象求值一次,而普通的是兩次,注意效率哦。9.自增自減運算符:
1.不要單詞的以爲自增自減只是爲了書寫簡潔。迭代器部分有大用。因爲有的迭代器不支持算術加減,但是支持自增自減。
10 .運算符
示例:
*p.func();
(*p).func();
二者辨析:不同。因爲.的優先級高於* ,所以上面的那個等價於:
*(p.func())
//錯誤的解引用行爲。
求值結果是左值還是右值取決於參與運算的對象是左值還是右值。11.條件運算符:
規定了求值順序。可以嵌套。
格式:
<span style="font-size:18px;">condition? expr1:expr2</span>
注意: expr1 和expr2 必須是關聯類型的表達式,包括同類型。
優先級:優先級比較低,注意用括號。
示例:
(grade>=90)?“excellent":((grade<60)?”fail":"good")
正 好劃分成三段,一般不要多層嵌套,難讀。
12.位運算符:
運算對象:整數類型,並且把對象看成是二進制位集合。
1.移位(<< >>0)左移補0,右移視情況而定。無符號是補0,有符號補0或者1.
2.求反,位與,位或,位異或。位異或運算規則:對應位只有一個1,纔是1,否則0.
重載改變不了結合律和優先級。所以可以參照輸出運算符。
13.sizeof 運算符:
格式: sizeof(type) sizeof expr
結合律:右結合。
結果類型: size_t.
示例:
sizeof *p;
* 和sizeof 優先級相同。根據右結合律,等價於:
sizeof(*p);
tips:sizeof 運算符不會實際求出運算對象的值,因此裏面即使放了一個無效指針也不影響。
C++11 規定類可以使用域運算符獲取類成員的大小,無需聲明對象獲取成員的操作。
sizeof 只可以求出類中固定部分的大小。關於這個可以看深度探索C++對象模型博文。
14.逗號運算符:
規定了求值順序。
15.類型轉換:
兩類:
隱式類型轉換和顯示類型轉換
一個原則:儘可能不丟失精度,所以一般往高精度轉換。
算術轉換:規則定義了一套類型轉換的層次:將運算對象轉換成最寬類型。
整形提升:小整形轉換爲能容納的最小大類型。小類型泛指bool,char 類型。
兩個情況:
同號整數:往高精度類型轉換。
不同號:具體情況依賴於機器。詳細請翻書。
這個地方說的號不是正負,是帶符號和不帶符號。
<span style="font-size:18px;"><span style="color:#330099;">3.12L+'a' ;</span>
//不要以爲'a'直接變成long double ,'a'先提升成int,然後轉換爲long double.</span>
四種強制類型轉換:<span style="font-size:18px;">1.static_cast<type>(expr)</span>
不包含底層const 的情況下,任何具有轉換意味的地方都可以用。
<span style="font-size:18px;">2.const_cast<type>(expr)</span>
號稱去底層const轉換。
<span style="font-size:18px;">3.dynamic_cast<type>(expr)
4.reinterpret_cast<type>(expr)</span>
tips: 強制類型轉換會干擾類型檢查,慎用。當用強轉時,會不在報警告信息。
舊式C風格轉換:
<span style="color:#330099;">1.type(expr);</span>
2.(type)expr;
16.習題解答:
4.1
<span style="font-size:24px;">Output: 105</span>
4.2<span style="font-size:24px;">a) *(vev.begin());
b) (*(vec.begin()))+1;</span>
4.3<span style="font-size:24px;">可以接受啊。前提是我的程序執行的結果是不受影響的。
因爲系統間的自己協調說不定比認爲規定更有效率。</span>
4.4
((12/3)*4)+(5*15)+((24%4)/2)
Output: 91
#include <iostream>
using namespace std;
int main(){
cout << 12 / 3 *4+5 * 15+24 % 4/ 2;
system("pause");
return 0;
}
4.5
a) -86 b) -18
c) 0 d) -2
4.6
int i;
i%2==0;
4.7
超出其容納範圍。
short var=32767;
var+=1;
-----
char x = 256;
---------
int v = INT_MAX;
cout << v+1;
4.8
邏輯與:當前僅當左邊求值爲真的時候纔對右邊進行求值。
邏輯或:當前僅當左邊求值爲假的時候纔對左邊進行求值
相等性運算符:未規定,由具體平臺決定。
4.9
先判斷cp是否是空指針,若cp是空指針,不用判斷右邊運算對象。
若cp不是空指針,那麼將對右邊進行求值。
4.10
int var;
while(cin>>var&&var!=42)
4.11
(a>b)&&(b>c)&&(c>d)
4.12
小於的優先級大於!=.
所以等價於:
i!=(j<k);
由具體情況i,j,k的值確定。
4.13
a) i=3; d=3.0;
b) i=3; d=3.5;
4.14
首先 i 未定義。
加上i 已經定義。
第一個if中, i不可能出現在42 不可能出現在等號左側。
第二個if中,if條件永遠爲真。
4.15
pi=0;
ival=0;
dval=0;
原因:指針類型無法轉換成int 型。
4.16
a)因爲賦值運算符優先級比較低。應該有括號括起來。
修改: if((p=getPtr())!=0)
b)誤用相等比較符號和賦值符號。
修改:if(i==1024)
4.17
前置自增:直接返回的自增的對象自身。
後置自增:返回的是自增的對象未修改之前的副本。
4.18
可能會無法輸出第一個元素。
甚至可能輸出一個不存在的元素。
因爲它的模型是用前一個元素的值進行判斷,但是輸出後一個元素的值。
4.19
a) ptr 非空的前提下先對ptr自增,然後解引用自增之前的指針。
如果ptr是空的,那麼後面的操作不會進行。
b) 先對ival進行後置自增,如果ival的值不是0.
那麼測試右邊運算對象的值,此時的ival已經是自增過的值。
因爲規定了求值順序,所以不影響。
c)未規定求值順序.
修改:vec[ival]<=vec[ival+1];
4.20
a) 合法。先對迭代器後置自增,然後解引用。
b) 合法。先解引用迭代器的值,然後對其值後置自增。
c) 不合法。修改:(*iter).empty();
d) 合法。 用迭代器調用string 的empty函數。
e) 合法。先解引用迭代器,然後對其值進行前置自增。
f) 合法。用自增的迭代器調用成員函數。這個地方有點疑問?
後來我上網查了下答案,答案是如此說的,iter先調用成員函數,然後加1.
還是符合優先級的規律的。
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
vector<string> svec = { "hi", "hay"};
vector<string>::iterator iter;
iter = svec.begin();
cout<< *iter << endl; //輸出hi
if (iter++->empty()){
cout << *iter << endl;
cout << "Empty";
}
else{
cout << *iter << endl; //輸出hay
cout << "Non-empty";
}
cout << endl << *iter << endl;
system("pause");
return 0;
}
4.21
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
vector<int> ivec = { 1, 2, 3, 4 };
for (auto &x : ivec){
if (x % 2 != 0)
x = 2 * x;
else
continue;
}
for (auto x : ivec)
cout << x << " ";
system("pause");
return 0;
}
4.22
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
int grade = 0;
cin >> grade;
while (grade < 0 || grade>100){
cout << "Wrong Input and Enter again" << endl;
cin >> grade;
}
cout << ((grade >= 90) ? "High Pass" :((grade >= 75) ? "Pass" :((grade >= 60) ? "Low Pass" : "Fail")));
system("pause");
return 0;
}
#include <iostream>
#include <vector>
#include <string>
using namespace std;
int main(){
int grade = 0;
cin >> grade;
while (grade < 0 || grade>100){
cout << "Wrong Input and Enter again" << endl;
cin >> grade;
}
if (grade >= 90)
cout << "High Pass" << endl;
else if (grade < 90 && grade >= 75)
cout << "Pass" << endl;
else if (grade >= 60&&grade < 75)
cout << "Low Pass" << endl;
else
cout << "Fail" << endl;
system("pause");
return 0;
}
4.24
題目有錯誤。因爲無法編譯通過是因爲用字符串和字符進行比較。
4.25
output:-1146. //暫時沒搞明白爲何會出現這個值。
4.26
因爲unsigned int 16佔 16位,無法表示所有人。
4.27
假設unsigned long 佔32 位:
//u11:0000 0000 0000 0000 0000 0000 0000 0011
//u12:0000 0000 0000 0000 0000 0000 0000 0111
//&
//0000 0000 0000 0000 0000 0000 0000 0011
//|
//0000 0000 0000 0000 0000 0000 0000 0111
u11&&u12:1;
u11||u21:1;
4.28
cout<<sizeof(char)<<sizeof(int)
<<sizeof(bool)<<sizeof(float)
<<sizeof(double);
4.29
ouput: 10,1.
#include <string>
#include <iostream>
using namespace std;
int main(){
int x[10], *p = x;
cout << sizeof(x) / sizeof(*x) << endl;
cout << sizeof(p) / sizeof(*p) << endl;
system("pause");
return 0;
}
4.30
a)(sizeof x)+y;
b)sizeof(p->mem[i])
c)(sizeof a)<b;
d)sizeof(f());
4.31
提高效率。
vector<int>::size_type cnt=ivec.size();
for(vector<int>::size_type ix=0;
ix!=ivec.size();ix++,cnt--){
ivex[ix]=cnt;
}
4.32
遍歷數組
4.33
根據SomaValue 的值確定程序執行。
若能求值最後轉換成布爾值1,那麼想x,y進行前置自增。否則x,y前置自減。
4.34
a) float 到int 到bool.
b)ival 到float,然後float 到double.
c)cval 到int,然後int到double.
4.35
a)有。'a'先提示爲int,然後轉成char.
b)有。ival 變爲double,然後ui轉換爲int.最後轉爲float
c)有。具體情況而定。
d)有。具體情況而定。因爲無符號有和符合碰到一起,要看所佔字節數進行轉換。
4.36
i*=static_cast<int>(d);
4.37
a)pv=reinterpret_cast<void*>ps;
b)i=static_cat<int>(*pc);
c)pv=static_cast<void*>(&d);
d)pc=static_cast<char*>pv;
4.38
把j/i的值強制轉換成double賦給slope。
後記:
真是寫了一下午啊。我從8點開始寫習題的,一直寫到11.30。