C++自修入門實境秀、C++ Primer 5版研讀秀 22/ ~頁150 4.6. The Member Access Operators-2...





4.4. Assignment Operators

The left-hand operand of an assignment operator must be a modifiable lvalue.

一個指定運算子的左邊運算元一定要是一個能改寫其值的左值(左址右值)

14:00初始化與指定的區別

頁145

15:20

字面值(literal)、算術運算式(arithmetic expressions)都是右值(rvalue):

literals are rvalues

arithmetic expressions are rvalues

常值就是不能更動的(nonmodifiable)

ci is a const (nonmodifiable) lvalue

31:00

只要會造成資料在轉型時的變化,用{}來作初始化器,就會行不通。(或者說幫我們檢查是否在初始化時會有資料的異動)

37:00

串列初始器(list initializer)可以是空的,編譯器會為左方欲初始化的值指定一個內定預設值。

Regardless of the type of the left-hand operand, the initializer list may be empty. In this case, the compiler generates a value-initialized (§ 3.3.1, p. 98) temporary and assigns that value to the left-hand operand.

Assignment Is Right Associative

指定會先處理右邊
52:00

int ival, *pval; // ival 是一個 int ; pval 是對 int 的一個指標

ival = pval = 0; //錯誤:不能將一個指標的值指定給一個int

「pval = 0」 是將pval指標設為空指標

等同於用NULL或nullptr來指定:

p = 0;

pNULL = NULL;

pnullptr = nullptr;

∴ 「ival = pval = 0;」是錯在最左邊的指定,不是最右邊的

string s1, s2;

s1 = s2 = "OK"; //字串字面值"OK"會被轉為string

字串字面值(string literal)的型別和string型別不是同一個喔!!

字串字面值(string literal)的型別應該是字元陣列(character arrays)型別

字串字面值(string literal)應該就是C-Style string

在Visual Studio中編譯器是把字串字面值(string literal)當作指向常值字元的指標

char* p{ "仰觀宇宙之大,俯察品類之盛" };

Severity Code Description

Error (active) E0144 a value of type "const char *" cannot be used to initialize an entity of type "char *"

上式改成「const char* p{ "仰觀宇宙之大,俯察品類之盛" };」就可以了。因為字串字面值(string literal)是不能更動的rvalue,所以須要用const

把「p」印出來

cout << p << endl;

就是:仰觀宇宙之大,俯察品類之盛

上式也等同於(或類似):

char ia[] = "仰觀宇宙之大,俯察品類之盛";

把ia印出來cout << ia << endl;也是「仰觀宇宙之大,俯察品類之盛」

1:25:00

char ia[] = "仰觀宇宙之大,俯察品類之盛";

const char* p{ "仰觀宇宙之大,俯察品類之盛" };

string s{ "仰觀宇宙之大,俯察品類之盛" };

cout << ia << endl;

cout << p << endl;

cout << s << endl;

以上3式印出來的結果是一樣的

可見字串字面值(string literal)是字元陣列(character arrays),可以轉換為string型別



頁146

1:54:00

Assignment Has Low Precedence

We can write this code more directly as Click here to view code image int i;

// a better way to write our loop---what the condition does is now clearer

while ((i = get_value()) != 42) {

// do something ...

}

我們可以用 更直接的方式撰寫這段程式碼:

int i;

//撰寫我們迴圈較好的方式,條件做些什麼事現在更清楚了

這就是拘泥字典的翻譯,毫無人味與親切感,類似機器翻譯

→寫這個迴圈更好的方式,條件式有什麼作用(或「條件式都做了什麼」,或「條件式在做什麼」)這樣就更清楚了

while ( (i = get_value()) ! = 42) {

//do something ...

}

「directly」翻成簡潔簡單明瞭或直覺精明更適當

中文的「直接」語義並無此處上下文的意思,這就是不學語義學、沒有語義學相關背景根柢之過也。

2:40:00 名豈文章著?官應老病休! 中文(母語)能力重要啊!別再等閒視之了

2:58:00

int get_value(int i) {

return i*2 ;

}

void test() {

int j{0};

while (get_value(j)!=42)

j++;

cout << j << endl;

}

2:59:40

Beware of Confusing Equality and Assignment Operators

相等運算子與指定運算子

「相等」在中文語境中不必加「性」,即是名詞。

3:4:30

The author of this code almost surely intended to test whether i and j have the same value:

可見「author」此字在本書有等同於程式設計師(程式碼撰作者 programmer)的意思

本書後面第三篇的標題「author」(Part III: Tools for Class Authors)中文版翻成「作者」在中文語境就不適當了。中國人(中文語境使用者)不會把「程式設計師」稱作「作者」!



頁147

3:16:50

Compound Assignment Operators

複合指定運算子(compound assignment operator)



3:31:00

複合指定運算子(compound assignment operator)可能比一般指定運算子(assignment operator)效能好。

In many, perhaps most, contexts this difference is immaterial aside from possible performance consequences.

練習4.13

3:40:00

int i; double d;

d = i = 3.5;//i=3;d=3.0000……

i = d = 3.5;//d=3.500……;i=3;

//可見double會將可儲存的空間都補完0,所以較佔記憶體空間

練習4.14

if (42 = i);//……error:字串字面值(string literal)is nonmodifiable rvalue

Severity Code Description

Error (active) E0137 expression must be a modifiable lvalue



if (i = 42);//…… i=42 is nonzero so (i=42)=true

Severity Code Description

Warning C6282 Incorrect operator: assignment of constant in Boolean context. Consider using '==' instead.

Some, but not all, compilers are kind enough to warn about code such as this example.

3:51:40

練習4.15

double dval; int ival; int* pi;

dval = ival = pi = 0;/* assignment is right associative

1.pi=0 is ok ,that is using 0 to initialize the pointer pi

2.ival = pi is illegal. can't assign a pointer to a int veriable

3.dval = ival is ok if ival=3 then dval will be 3.0000……

*/

可更正為:

double dval; int ival; int* pi;

//dval = ival = pi = 0; // correct to:

dval = *(pi = &(ival = 0));

//dval = *(pi = &(ival=0));/*this will be equivalent to

// dval=ival;*/

/*if ((dval=*(pi=&(ival=0)))== (dval = ival))

{

cout << "OK!" << endl;

}*/

若「0」不變,主要是要先將0指定給int的整數ival,再取址指定給整數指標pi,再解參考指定給dval

練習4.16

/*

(a)if (p = getPtr() != 0)

(b)if (i = 1024)*/

/*

int p;

if ((p = getPtr()) != 0)

cout << "that's it!" << endl;*/

int i{1024};

//if (i = 1024)// i has value of 1024,nonzero,then 'if' always is true

if(i==1024)

cout << "3q" << endl;

4:21:50

4.5. Increment and Decrement Operators

4:28:00

This notation rises above mere convenience when we use these operators with iterators, because many iterators do not support arithmetic.

與迭代器並用時,這些記號的好處就不僅限於方便了,因為許多迭代器並不支援算術運算。

rises above mere convenience言下之意是當我們將這種遞增遞減運算子和迭代器(iterator)一起使用時,所考量的就不僅僅只是便利或方便而已了。就像不提供<=等的關係運算,因為許多的迭代器(iterator)也都不支援算術運算。

頁148

4:33:00

前綴與後綴是有不同的,前綴會改變物件的值

而後綴不會更改:

So far, we have usedonly the prefix form. This form increments (or decrements) its operand and yields the changed object as its result. The postfix operators increment (or decrement) theoperand but yield a copy of the original, unchanged value as its result:

目前為止,我們只用過前綴形式。 這種形式會遞增(或遞減)其運算元,然後產出變更過後的物件作為結果。後綴運算子則是 遞增(或遞減)其運算元,但產出的結果是原本尚未變更的值的一個拷貝:

int i = 0, j;

j = ++i; // j = 1, i = 1: prefix yields the incremented value

j = i++; // j = 1, i = 2: postfix yields the unincremented value

These operators require lvalue operands. The prefix operators return the object itself as an lvalue. The postfix operators return a copy of the object’s original value as an rvalue.

int i = 0, j;

j = ++i; // j = 1, i = 1 prefix產出遞增過後的值

j = i++; // j = 1, i = 2 postfix產出未遞增的值

這些運算子都要求lvalue的運算元。前綴運算子回傳物件本身作為一個lvalue。後綴運算子 回傳該物件原本的值的一個拷貝作為一個rvalue。

int i = 0, j;

j = ++i;

j = i++;

j = i++;

j = ++i;

要注意「=」是assingment operator(指定運算子)!

運算子 傳回值

前綴 運算元已變之本身 左值

後綴 運算元最近一次未變的原值 右值

Advice: Use Postfix Operators only When Necessary

沒有必要不要用後綴的

4:57:30(以下應該是沒有錄到,哈哈,就看臉書直播第73集吧)

extra work 冗餘的事(工作)

extra 冗餘、多餘、沒必要

後綴運算子必須儲存原本的值,才能夠回傳那個未遞增的值作為其結果。

所以後綴是比前綴多做了一份工作(一件事),就是保存下來未遞變前的版本

4:58:28

Combining Dereference and Increment in a Single Expression

5:8:00

auto pbeg = v.begin();

// print elements up to the first negative value

while (pbeg != v.end() && *pbeg >= 0)

cout << *pbeg++ << endl; // print the current value and advance pbeg

auto pbeg = v.begin();

//印出第一個負值之前的元素

while (pbeg != v.end() && *beg >= 0)

cout << *pbeg++ << endl; //印出目前的值,並推進pbeg

beg→pbeg,掉了一個「p」,中文版也沒訂正

we can use postfix increment to write a loop to print the values in a vector up to but not including the first negative value:

我們可以使用後綴遞增來撰寫一個迴圈,印出一個vector中第一個負值之前的值 (不包括那個負值):

印出一個vector中第一個負值之前的值 →在一個vector中印出它第一個出現負值元素前的所有元素值。

在向量中打印值,但不包括第一個負值:

vector<int>v{ 1,2,22,-4,5, };//多一個「,」發現美麗的錯誤!

auto pbeg = v.begin();

while (pbeg != v.end() && *pbeg >= 0)

cout << *pbeg++ << endl;

5:27:00

頁149

This usage relies on the fact that postfix increment returns a copy of its original,unincremented operand. If it returned the incremented value, we’d dereference the incremented value, with disastrous results. We’d skip the first element. Worse, if the sequence had no negative values, we would attempt to dereference one too many elements.

這種用法仰賴「後綴遞增會回傳其原本的、未遞增的運算元的一個拷貝」這個事實。如果它 回傳的是遞增後的值,我們就會對那個遞增過的值解參考,導致災難性的結果。我們會跳過 第一個元素。更糟的是,如果該序列沒有負值,我們就會多解參考一個元素。

這才是作者「disastrous results 、Worse」的意思,最後一次++的位移,試圖存取一個不存在的元素

vector<int>v{ 1,2,22,4,5, };//當都沒有負值時

auto pbeg = v.begin();

while (pbeg != v.end() && *pbeg >= 0)

cout << *(++pbeg) << endl;



如果vector中有負值,它最多是把那個負值也多印出罷了(當然,也跳過了第一個元素沒做):參見練習4.18

vector<int>v{ 1,2,22,-4,5, };

auto pbeg = v.begin();

while (pbeg != v.end() && *pbeg >= 0)

cout << *(++pbeg) << endl;





5:43:50

Advice: Brevity Can Be a Virtue

簡潔是種美德(和國文作文一樣!)





Once the notation is familiar, writing

cout << *iter++ << endl;

is easier and less error-prone than the more verbose equivalent

cout << *iter << endl;

++iter;

也就是說多做多錯、少做少錯,不做不錯。呵呵

「++」怎麼跑到前綴去了!?因為已經列出未遞增的iter,所以這裡要用前綴,以便接下來的條件式來判斷。

vector<int>v{ 1,2,22,-4,5, };

auto pbeg = v.begin();

while (pbeg != v.end() && *pbeg >= 0)

{

cout << *pbeg << endl;

++pbeg;

}



值得花功夫把這種表達式來弄熟悉

6:0:00

Remember That Operands Can Be Evaluated in Any Order



切記運算元被評估的順序是不一定的

6:21:00 「未定義的」就好像語義學中所謂含糊、含混的謬誤

頁150

6:30:10

練習4.17

前綴與後綴的差異

前綴傳回一個運算元已經遞增的左值

後綴則傳回運算元未遞增的右值

練習4.18

6:35:00

前綴遞增運算子_練習4.18解答

練習4.19

int ival=-1;

int* ptr = &ival;

if (ptr != 0 && *ptr++)

cout<<3<<endl;

/*

(a)ptr != 0 && *ptr++

*/

雖然ptr會被prt++異動,但不影響最後真假值(truth value)的評估

7:15:00

int ival=-111,i=0;

while (ival++ && ival)

cout<<3<<" "<<++i<<endl;

cout << ival << endl;

/*

(b)ival++ && ival

*/

7:33:00

int ival = 0,i=0;//ival不能從小於-1開始

vector<int>vec{1,2,3,4,5,6,7,8,9,10};

while (ival!=9 && vec[ival++] <= vec[ival])

cout<<vec[ival]<<" "<<++i<<endl;

cout << ival << endl;

/*

(c) vec[ival++] <= vec[ival]

*/

4.6. The Member Access Operators

4.6成員存取運算子

留言

熱門文章