C++ Primer 筆記+習題解答(四)

今天是第四章節的總結,挺快的,因爲我翻了翻書,還有那麼多沒看,頓感焦急,故趕忙開始寫總結。昨天在微信上看見一篇文章,大意是談焦慮的壓力,所以我也在提醒自己,要在高質量的前提下重視效率,不能因爲時間不充足就忽略了質量。

有錯誤 請指正 謝謝

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。

End

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