C++自修入門實境秀、C++ Primer 5版研讀秀29/ ~5.3.2. The switch Statement-20190813_151312
頁168
Chapter Summary
C++還賦予了運算子多載(重載)的特權,這使我們在寫自己的類別型別時可以就既有的運算子符號去賦予它更多重的意義與功能,所以叫多載或重載(overload)。在14章會談到自己重新定義的運算子,是怎麼做到的
想要弄清楚運算式是什麼東西,弄熟了運算子的定義與功能是不夠的,還得知道運算子的優先權與結合序(結合性)以及運算元們被評估估算的次第。
Precedence determines how operators are grouped In a compound expression.
優先權 決定了 運算子 是 怎麼 在 複合的 運算式 中 被 群組化歸類的。
Associativity determines how operators at the same precedence level are grouped.
結合性 則決定了 有著 同等 優先權的 運算子之間 是 怎麼 被 群組化歸類的
因為編譯器要怎麼評估沒有制定評估順序的運算子的運算元,是不受限制的,所以當一個運算式中有二個以上的運算元參考到同一個物件,而剛好又有至少一個運算元會改變那個物件,那麼萬一編譯器的估算影響了我們預期的結果,要找到這種問題的癥結會是非常困難的。到時候會搞不清楚為什麼運算式不如我們預期的那樣運作,而出乎我們意料之外。所以只要有這種困惑時就應該想想是不是在運算元被評估的順序上出了岔子。
所以本章討論運算式是從運算子與運算元這二大部分分論,再合論的。
運算元在運算時會自動轉型(自動轉為相關型別)
小的整數值型別會自動被升等為大的整數值型別,這在任何運算式中都沒有例外的
內建型別和類別型別(class type)都具有型別轉換(type conversion)的特性或功能
使用「cast」來對運算元做明確地轉型(重鑄(cast))
20:17
Defined Terms
已定義的詞彙,已完成定義的詞彙,就是已學習、學過的意思,也就是「已學到的觀念或知識」或「已學會的技巧或能力」 2:25:30
arithmetic conversion
算術型別間的轉換
為了保住精準度(precision),算術型別轉換傾向將小的型別轉換成大的型別;即整數會轉成浮點數
超過一個以上運算子的運算式都叫複合運算式(compound expression)
33:50
expression
expression The lowest level of computation in a C++ program.
運算式即c++程式碼中最根本的單元運算
頁169
39:10
即使在看來並無轉型必要的地方,小的整數值型別(如char,short)也會自動轉成int
41:00
左值(lvalue)
左值實際上是一個expression,而且是會產生物件或函式的expression表達式(運算式):1:28:40
An expression that yields an object or function. A nonconst lvalue that denotes an object may be the left-hand operand of assignment.
lvalue會產出一個物件或函式的運算式。表示一個物件的非const lvalue可以是指定(assignment)的左運算元。
一個會產生物件或函式的運算式 →這就叫左值(lvalue)嗎?什麼叫運算式,不要想到只是那種有運算子的;一個變數,也可以是一個運算式。因為運算式,英文是「expression」也是表達式的意思,可以表達一個意思(物件)的,當然也可以算是「expression」,即有所表意的意思。
一個非常值的左值(lvalue)即指一個物件可能是一個指定的左運算元
這裡都沒有提到「址」的概念!
表示一個物件的非const lvalue可以是指定(assignment)的左運算元
意思就是一個指定式的左運算元即是一個物件的非常值的左值(lvalue)
57:00
operand
運算元、操作數、運算值
Values on which an expression operates.
1:2:10
order of evaluation
雖然編譯器可以自由地估算沒有預定順序的運算元與運算子,然而
運算元永遠都會在運算子本身被估算之前估算。
the operands are always evaluated before the operator itself is evaluated.
所以編譯器估算運算式是先運算元,再運算子的
1:4:02
只有&& || ?: ,
這四個指定了其運算元被估算(結合)的順序
1:7:00
overloaded operator
Version of an operator that is defined for use with a class type.
overloaded operator (重載的運算子)一種版本的運算子,定義來與某個類別型別並用。
這裡是說「多載的運算子」不是「多載的符號」,所以如「*」「,」在C++內建型別中,作為運算子是沒有多載的,但作為符號是有的
這種運算子又被類別設計者拿去另行定義在他制定的類別型別上可作怎樣的運算或處理,這時,這種運算子就是這裡所謂重載或多載(overload)版本的運算子
如生活中的多用途、多功能之義也。
不對啊,「*」同時定義為乘法、解參考(dereference)運算子,則內建仍有多載運算子也!
1:18:00
reinterpret_cast
Interprets the contents of the operand as a different type.Inherently machine dependent and dangerous.
reinterpret_cast將運算元的內容解讀為不同的型別。本質上取決於機器,並且是危險的。
注意!其他地方講轉型都是用「cast」「conversion」「transform」這類的字,而這裡特意用「interpret」就是意謂著與真正的轉型是有所不同的。這裡只是「interpret the contents……as(當作)……」並不是converse 或 cast 或 transform the type to ……只是當作而已,沒有真的變性!(就像人妖未變性也)
是「將內容解讀為」,不是「將型別轉變為」
只是將內容(所載物)一視同仁,視為一同,實際上並沒有做轉型的動作。
1:29:59
右值(rvalue)
果然和左值一樣都是「expression」表達式、表示式、表示法:
Expression that yields a value but not the associated location, if any, of that value.
會產出一個值,但並非那個值所關聯的 位置(如果有的話)的運算式。
short-circuit evaluation 短路估算
1:36:00
Term used to describe how the logical AND and logical OR operators execute. If the first operand to these operators is sufficient to determine the overall result, evaluation stops. We are guaranteed that the second operand is not evaluated.
sizeof
1:37:16
Operator that returns the size, in bytes, to store an object of a given type name or of the type of a given expression.
1:47:40
& operator 位元AND運算子
這也是多載的運算子(另一重功能是定義為「取址運算子」(address of)
Bitwise AND operator. Generates a new integral value in which each bit position is 1 if both operands have a 1 in that position; otherwise the bit is 0.
頁170
1:56:10
Note: Iterators have ++ even if they do not have the + operator.
迭代器(iterator)雖然沒有「+」運算子,但它們有定義「++」遞增運算子
2:1:00
<< operator The left-shift operator.
這和>>也是多載的運算子,只不過在iostream中定義的,是在程式庫中,並非C++語言本身內定的
如「*」和「&」就決定是C++語言內定的多載運算子了。
2:6:20
Left-hand operand should be unsigned; if the left-hand operand is signed, it is undefined if a shift causes a different bit to shift into the sign bit.
左運算元應該 是unsigned的;如果左運算元是signed的,如果移位導致不同的位元移到正負號位元(sign bit)那結果會是未定義的。
如果→要是。
2:11:30
>> operator The right-shift operator. 右移運算子
If the left-hand operand is signed, it is implementation
defined whether bits shifted into the result are 0 or a copy of the sign bit.
如果左運算元是signed 的,移到結果中的位元是0或正負號位元的一個 拷貝,就由實作來定義。
2:14:00
~ operator
Bitwise NOT operator. Generates a new integral value in which each bit is an inverted copy of the corresponding bit in the (possibly promoted) operand.
由(possibly promoted)可以知道,編譯器在評估時很可能是對運算元先進行型別轉換的評估,再作其他評估
2:20:00
頁171
Chapter 5. Statements
2:24:00
2:28:10
Statements are executed sequentially.
循序漸進地執行
2:31:10
flow-of-control 怎麼翻? 這裡是作形容詞
the flow of control 英文 這裡是作名詞
流程控制 中文才是名詞
控制流程 中文是動詞
控制流程的 中文:形容詞
2:38:00
Word VBA 臉書直播程式碼改進,找到沒有超連結的部分,將之選取起來,並自動插入超連結 2:59:00
頁172
2:36:11 2:59:40
5.1. Simple Statements
3:6:00
More commonly, an expression statement contains an expression that has a side effect —such as assigning a new value to a variable, or printing a result—when it is evaluated.
這句應是解釋上面第二個例子:
cout << ival; // useful expression statement
以作為其前例的對照
所以中文這個side effect 不可以翻成「副作用」,因為中文語境中「副作用」有不好而想要去除的意思,這裡並不是否定這些「effect」的價值,反而更是more commonly常見的常態,那麼這個「side effect」就當翻成是
隨帶或連帶的作用(效果)
才是。也就是當這樣的運算式述句(expression statement)被評估的同時,連帶地也會帶出下述的那些動作。
比較常見的是,一個運算式述句所含有的,是被估算時會有副作用(side effect)的一個運算式,例如指定一 個新的值給某個變數,或印出一個結果。
這就是翻譯時沒有注意中文語境、語義學、語用學的問題,而只注意字義與語法的結果。是「連帶的作用」,不是「副作用」。
中文程度,能不好嗎?!只有外文能力強,能怎樣?
Null Statements
3:17:20
空述句(empty statement)
沒有任何內容,只有一個分號的,就是空述句
A null statement is useful where the language requires a statement but the program’s logic does not.
在語言要求一個述句,但程式邏輯並不需要的地方,null述句就能配上用場。
等於是搪塞用的嘛 呵呵。
配上用場→派上用場(可能是用注音輸入法的緣故)
Best Practices 養成好習慣
寫空述句的時候一定要加上註解,說明這空述句是幹嘛用的
3:29:40
Beware of Missing or Extraneous Semicolons
小心遺漏或多餘的分號
頁173
3:34:40
3:44:40
Compound Statements (Blocks)
複合述句(區塊)
3:51:00 所以C++語言定義的語法述句單位是與實作程式時的語法述句單位不同的:
The logic of our program needed two statements but a while loop may contain only one statement.
一個空的區塊(block)就等同於一個空述句:
An empty block is equivalent to a null statement:
頁174
練習5.1
3:56:50
練習5.2
練習5.3
4:5:00
int sum = 0, val = 1;
// keep executing the while as long as val is less than or equal to 10
while (val <= 10)
sum += val,
++val;
std::cout << "Sum of 1 to 10 inclusive is "
<< sum << endl;
可讀性會降低,但真的很精簡
其實在C++語言中,對述句(句子)來講,「;」分號就是句號,而逗號依然是逗號(表示句子還沒講完,不成一個句子述句)
所以在這裡C++語言定義的while述句迴圈中只能包含一個述句的情境下,可以用「,」逗號運算子合併成只是一個述句而已。因為逗號運算子會依序從右至左評估,且只會傳回最後最右運算式的結果值,這個值正好供while條件式來用。
4:10:55
5.2. Statement Scope
4:12:20
控制結構(control structure)
變數的生命週期
4:17:40
練習5.4
//(a) while (string::iterator iter != s.end()) { /* . . . */ }
string s{("先天下之憂而憂","後天下之樂而樂")};//「,」在此應是逗號運算子,傳回的應是「後天下之樂而樂」
string::iterator iter{s.begin()};
while (iter != s.end()) { ++iter; /* . . . */ }
4:29:30
4:46:30原來同在一個scope裡頭調用時仍然有先後順序,必須先宣告,再調用,不能宣告不能寫在調用的前面
string word = "雲";
string wd{ "不畏浮雲遮望眼,自緣身在最高層" };
string::iterator siter=wd.begin();
bool findout(string w) {
if (wd.find(w)>0)
{
if (siter!=wd.end ())
word=*(siter++);
else
return false;
return true;
}
else
{
if (siter != wd.end())
word = *(siter++);
else
return false;
return false;
}
}
void test() {
//(b) while (bool status = find(word)) { /* . . . */ }
//if (!status) { /* . . . */ }
bool status =true;
while (status) { status = findout(word);/* . . . */ }
if (!status) { cout << 1 << endl; /* . . . */ }
}
「find」前面都沒提過,但C++裡有,是否是更新的標準(在C++11以後,故作者未及耶?)
5.3. Conditional Statements
if
switch與整數運算式(integral expression)有關
頁175
5:9:25
5.3.1. The if Statement
頁176
5:23:40
Nested if Statements
5:31:40
Watch Your Braces
頁177
5:38:10
Dangling else 懸置的else
5:41:01
referred to as 可以翻成「所謂的」
5:47:10
In C++ the ambiguity is resolved by specifying that each else is matched with the closest preceding unmatched if.
C++是藉由規定一個else是與它最近的if作匹配的,也就是說一個if如果沒有else,那麼最接近這個 if 的else 就是和它成對的else,如果那個if已經有else,就再往外找最靠近而落單的if來配成對。
Controlling the Execution Path with Braces
用大括號來控制執行的軌跡
在及有大括號來控制(具名、明顯宣告)執行範圍與流程時,C++是以上述的方式,配對if和else的
6:2:00
頁178
6:5:00 6:20:27
練習 5.5
練習 5.6
練習 5.7
////(a) if (ival1 != ival2)
//// ival1 = ival2
////else ival1 = ival2 = 0;
//int ival1 = 1, ival2 = 1;
//if (ival1 != ival2)
// ival1 = ival2;
// else ival1 = ival2 = 0;
//int ival = 1,minval =10,occurs=0;
//if (ival < minval){
// minval = ival;
// occurs = 1;
//}
////(b) if (ival < minval)
//// minval = ival;
////occurs = 1;
//int ival=0;
//if (int ival = get_value())
// cout << "ival = " << ival << endl;
// if (!ival)
// cout << "ival = 0\n";
////(c) if (int ival = get_value())
//// cout << "ival = " << ival << endl;
////if (!ival)
//// cout << "ival = 0\n";
int ival=0;
if (ival== 0)
ival = get_value();
//(d) if (ival = 0)
// ival = get_value();
6:43:40
練習 5.8
Personalize Visual Studio with custom keyboard shortcuts https://youtu.be/yxg6Cprw5qg
Visual Studio 2019 指定鍵盤快捷鍵
6:10:50
Visual Studio 2019 and GitHub
6:11:40
同步 sync
按下「pull」來取得在GitHub上最新的資源檔
6:13:30 Team Git Pull
6:15:50 設定快捷鍵的套用範圍,若不是global(全域性)的,則其優先權是較全域的高,可以蓋過全域的
5.3.2. The switch Statement
頁179
switch(expr)……case……break;
相當於VBA的select ……case……else……
switch 是關鍵字,而case是標籤(label)
The case keyword and its associated value together are known as the case label .
6:46:10 7:7:10 case仍是keyword,和它後面帶的值連在一起才叫「label」
case labels must be integral constant expressions
所以expr才須轉成整數型別 8:36:40
The expression is converted to integral type.
break statement 7:0:58
正因break是個述句,所以後面要加分號「;」
頁180
Control Flow within a switch
在一個swith區塊裡的控制流程(控制執行的流程)
execution flows
⑧找對主詞 誰的流程、什麼流程?程式執行的
每個case 僅能有一個單一的值:
Each case label can have only a single value, but……
省略掉break述句,是為了讓程式能夠執行多重case label的值
fall through
中文版竟然翻成「落經」,也太文言了吧。不如翻成「貫徹」或「貫行」(貫穿執行)就好。「魚貫而行」也不錯。
或只翻成「行經」就好。
頁181
7:33:00
養成好習慣(Best Practices)
Omitting a break at the end of a case happens rarely. If you do omit a break, include a comment explaining the logic.
如果有需要省略break;述句,就當註為何必須如此做
7:39:50
The default Label
既然是個標籤(label),後面就要接一個「:」冒號
7:43:00
execution will start at the default label
程式的執行 會 從 那個 default label 開始。
中英文原來也是可以④倒序重組 就讀懂的!九陽神功還能小覷嗎?
7:48:00
頁182
養成好習慣Best Practices
It can be useful to define a default label even if there is no work for the default case. Defining an empty default section indicates to subsequent readers that the case was considered.
定義一個空的default段落等於向後續的讀者表明我們有考慮過那種情況。
總之,該寫的不要貪圖方便就隨意省略,最後一個case的break;還有defaule:標籤,都不宜省略掉
「我們」⑧找對主詞 ⑦先抓動詞 動詞consider的主詞
一個標籤(label)不能獨立存在,它後面一定要跟著一個述句,即使是空述句或空區塊;否則也一定要接著另一個標籤,如前面所示省略了break;的那種
Variable Definitions inside the Body of a switch
8:7:30
As a result, the language does not allow us to jump over an initialization if the initialized variable is in scope at the point to which control transfers.
結果就是,如果那個有初始化的變數在控制要轉移至之處是在範疇內,此語言就不允許我們跳過初始化。
總之,不能用沒有初始化的變數,也不能用未宣告過的變數
如果非得到要在一個case區塊中定義並初始化其內的區域變數:
If we need to define and initialize a variable for a particular case , we can do so by defining the variable inside a block, thereby ensuring that the variable is out of scope at the point of any subsequent label.
如果我們需要為某個特定的case定義並初始化一個變數,我們可以藉由在一個區塊內定義該變數來這麼做,如此能夠確保那個變數在後續任何標籤所在位置都是範疇外的。
定義這樣的變數,就是要確保它對於後面任何標籤來說,都是在區域外的才行;即不能在後續標籤中再調用這個變數,這個變數對那些標籤來說,都是區域外的變數,即其生命調期對它們來講已經結束了。
block就是大括號括起來的部分,超出那個大括號的範疇,就是失效的了(生命週期結束的了)
所以關鍵就在{}!
case true:
{
// ok: declaration statement within a statement block
string file_name = get_file_name();
// ...
}
8:23:00
這樣就可以看出「body」和「block」的區別在哪裡了。用大括號括起來的區塊叫「block」。而「body」則未必有大括號括起
8:25:50
頁183
練習5.9
8:28:10
int vowelsCnt = 0;
char a;
while (cin>>a)
{
if (a=='a')
{
++vowelsCnt;
}
if (a=='e')
{
++vowelsCnt;
}
if (a == 'i')
{
++vowelsCnt;
}
if (a == 'o')
{
++vowelsCnt;
}
if (a == 'u')
{
++vowelsCnt;
}
}
cout << vowelsCnt << endl;
8:33:11
練習5.10
char a;
int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
while (cin>>a)
{
a = tolower(a);//參見中文版頁92,表3.3 cctype函式
switch (a)
{
case 'a':
++aCnt;
break;
case'e':
++eCnt;
break;
case'i':
++iCnt;
break;
case'o':
++oCnt;
break;
case'u':
++uCnt ;
break;
default:
break;
}
}
cout << aCnt << '\n'
<< eCnt << '\n'
<< iCnt << '\n'
<< oCnt << '\n'
<< uCnt << '\n' << endl;
8:51:50
char a;
int aCnt = 0, eCnt = 0, iCnt = 0, oCnt = 0, uCnt = 0;
while (cin>>a)
{
switch (a)
{
case 'A':
case 'a':
++aCnt;
break;
case 'E':
case'e':
++eCnt;
break;
case 'I':
case'i':
++iCnt;
break;
case 'O':
case'o':
++oCnt;
break;
case'u':
++uCnt ;
break;
default:
break;
}
}
cout << aCnt << '\n'
<< eCnt << '\n'
<< iCnt << '\n'
<< oCnt << '\n'
<< uCnt << '\n' << endl;
8:54:40
留言