C++自修入門實境秀、C++ Primer 5版研讀秀 33/ ~第6章函式·參數引數頁218 Passing a Multidimension...





自動物件(Automatic Objects)
⑧找對主詞 什麼東東自動?
0:25 修訂之前Word VBA選取區自動插入超連結的程式,以判斷剪貼簿內是以「」開頭的內容才執行
7:20
The objects that correspond to ordinary local variables are created when the function’s control path passes through the variable’s definition. They are destroyed when control passes through the end of the block in which the variable is defined.
Objects that exist only while a block is executing are known as automatic objects.
這裡的control都類似「execute」
path可翻成軌跡、路線、流程
「自動」什麼?⑦先抓動詞 什麼「自動」? ⑧找對主詞
自動 create 自動 destroy
物件自動被創建與銷毀

文言叫:自動物件
白話叫:自動被創建與銷毀的物件
∴只有中文要用「古文」或「文言」嗎??
各專業都有它自己的文言文。俗稱「術語」或「行話」,甚至「黑話」。

這裡「correspond to」應是翻成「照著」,在中文語境會比較清楚。
意指那些物件是照著區域變數的定義來創建的。區域變數的定義就好像是藍圖,而被建造出來的物件,就好像是產品一樣。又如:28:05
Automatic objects corresponding to the function’s parameters are initialized by the arguments passed to the function.
參數就如藍圖,而對應(或照著)參數定義而產出的物件(就是自動物件)猶如產品(產出的物品)。「corresponding to the function’s parameters」是「Automatic objects」的後位修飾。
Automatic objects corresponding to local variables are initialized if their definition contains an initializer.
這三句的「correspond」對照起來看就更明白是什麼意思了。if在此翻成「只要」。

所以參數就是自動物件:
Parameters are automatic objects.
且區域變數也是自動物件。

注意這裡不用「block」而用「body」:
Parameters are defined in the scope of the function body.
因為參數並不是在大括號中

內建型別的區域變數——不像函式外的變數——其預設初始化是未定義的!
36:28
Local static Objects
區域static物件
區塊static變數(頁206)1:24:00
區域靜態物件
靜態區域變數
所謂靜態,就是不會自行初始化的,而是與它所在範疇一起被創造並初始化的。如全域的靜態,則是整個應用程式創建時就與之創建並初始化,而若是區域的靜態,則是該區域被創建時就與之被創建並初始化了。
It can be useful to have a local variable whose lifetime continues across calls to the function.
We obtain such objects by defining a local variable as static.
有時候你可能會需要其生命週期可以在不同函式呼叫間存續的區域變數。
⑦先抓動詞 call應是名詞,此是複數。continues才是動詞
across 修飾continues的狀態,是能夠穿越數次的函式呼叫。是指對單一函式(即該靜態區域變數所在者)呼叫、調用了多次。
也就是這種區域變數只要函式一被呼叫,它就備便了。不必再另行初始化。所以下文才有這樣call幾次的示例:55:52
As a trivial example, here is a function that counts how many times it is called:(頁206)
這裡中文版的翻譯也是大有問題的!「不同函式呼叫間」當改為「同一函式不同次的呼叫」才是。
只要是涉及static 就是一經在記憶體配置後,在整個應用程式還未結束時,它都有效(existing)、都存在著,隨時備便待用亦可用!故下文(頁206)有:1:20:00
Whenever count_calls is executed, the variable ctr already exists and has whatever value was in that variable the last time the function exited.
所以「靜」,就是「靜」在記憶體中靜止不動不死的意思。好像生命週期被凍結了,與應用程式相終始。
Each local static object is initialized before the first time execution passes through the object’s definition.
每個區域static物件都會在執行第一次經過物件的定義時被初始化。
注意「before」,可見,是在一調用函式時就被配置好的了,不是執行到了它的定義述句時才被初始化。中文版「時」當改為「前」才是!
可見編譯器或執行機制是先掃描應用程式全域或函式全區一遍,見到有static就先予配置處理,才做後行的操作。
static有點最先到(cretated)、最後走(destroyed)的意思:
Local static s are not destroyed when a function ends; they are destroyed when the program terminates.

頁206
size_t count_calls()
{
static size_t ctr = 0; // value will persist across calls
return ++ctr;//static的宣告與定義只會被執行一次,所以第二次呼叫此函式時,就從此行開始遞增ctr
//非靜態的就要每次呼叫都要再執行一次宣告和定義。或許就意義而言,也算是「動」態吧。「靜」就是做一次,就靜下來,終身不動了。動,就是不能吃老本,得一輩子辛苦勞動。每次用,每次要再做一次。好像吃飯用具(餐具);而靜態的,好像餐廳(可能一輩子也不用改建)。
}
int main()
{
for (size_t i = 0; i != 10; ++i)
cout << count_calls() << endl;
return 0;
}

Before control flows through the definition of ctr for the first time, ctr is created and given an initial value of 0 . Each
這裡也有「before」啊,總不能再漏掉了吧!
中文版這個地方是不是叫助理還是學生翻的呀:
這個程式會印出從1到10的數字,包括兩端。
→這個程式會印出包括1到10的一序列數字。
或者→ 這個程式會印出1到10的數字,包括1和10。
在控制第一次流經ctr的定義之前,ctr就會被創建並被賦予一個0的初始值。
→在控制流(即執行)第一次經過ctr的定義之前。
這裡翻成「前」前面翻「時」可見是疏忽了。

1:26:18
練習6.6
Give an example of a function in which each might be useful.
各自給出一個函式為例,說明它們何時可能會有用處。
應該是翻成「給一個函式為例來說明這三者在其中的作用」才是吧?!
//i is parameter,j is local verible, ctr is local static verible
size_t count_calls(size_t i)
{
static size_t ctr = 0; // value will persist across calls
size_t j = 1;
ctr=( j *= i);
return ctr;
}
int main()
{
for (size_t i = 0; i != 10; ++i)
cout << count_calls(i) << endl;
return 0;
}

練習6.7
和前一題是一樣的
1:42:00
size_t count_calls(size_t i)
{
static size_t ctr = 0; // value will persist across calls
ctr= i;
return ctr;
}
int main()
{
for (size_t i = 0; i != 100; ++i)
cout << count_calls(i) << endl;
return 0;
}
1:43:00
6.1.2. Function Declarations
2:31:40
總結就是:函式宣告與標頭檔的關係
函式宣告應該是放在標頭檔中
而要用到該函式的源碼檔(source file)則應引入 #include 該標頭檔
函式宣告就是沒有函式主體(body)的函式形式

1:45:30
函式與變數一樣,可宣告多次,但僅能定義1次!
As with variables (§ 2.2.2 , p. 45 ), a function may be defined only once but may be declared multiple times.
除了頁603的虛擬函式外,函式可以懸而不用(宣告了而不定義,只要不使用它就沒事)
要用(調用的用,即呼叫)的話,就必定要先定義好才得使用
函式宣告與函式定義的差別僅在函式宣告是沒有body(主體、本體)部分的。其餘「型別、名稱、參數列()」俱全。
函式宣告以分號來取代了其主體:
In a declaration, a semicolon replaces the function body.
這裡都用body,因為沒有{}了(尤其是其中的內容——述句),就不會有block。block就是multi-statements的同義詞:
compound statement Synonym for block.(頁199已定義的詞彙)
故一個函式宣告即沒有述句的函式或只有空述句的函式(空述句即單一分號「;」)
2:00:00
頁207
函式介面
好像也叫簽章,在 C# 中
These three elements—the return type, function name, and parameter types—describe the function’s interface.
三元素:傳回型別,函數名稱,參數型別
函式宣告也叫做「函式原型」
Function declarations are also known as the function prototype .
原型或初型、基模(基本模型)、基型,就是什麼都沒有的,只具備骨幹框架而已
Function Declarations Go in Header Files
函式宣告放在標頭檔中
就像之前在Visual Studio2019習作時,它自動幫我們建立的函式定義(宣告)一樣,會自動建立一個同名於目前.pcc檔的標頭檔(.h)

2:13:00 標頭檔可以視為「聯邦或中央」一個標準,以統一各個源碼檔(原始碼檔案),以免各檔各自為政、各唱各的調而不一致
2:24:00在Visual Studio 2019中源碼檔(
原始程式檔source file)翻成了「來源檔」,而另有「資源檔」(可能是resource file)

2:28:00 養成好習慣
源碼檔(source file)定義了一個函式,就該引入 #include 宣告那個函式的標頭檔



2:34:08

練習6.8

Write a header file named Chapter6.h that contains declarations for the functions you wrote for the exercises in § 6.1 (p. 205 ).

練習6.3

size_t count_calls(size_t i)

{

static size_t ctr = 0; // value will persist across calls

ctr= i;

return ctr;

}

int factoral(int f) {

int r = 1;

for (size_t i = 1; i <= f; i++)

{

r *= i;

}



return r;

}

void fact() {

int r = 1, f;

cout << "請輸入一個數字以求其階乘" << endl;

cin >> f;

for (size_t i = 1; i <= f; i++)

{

r *= i;

}

cout << r << endl;

return;

}



void absv(int ab)

{

unsigned r = 1;

if (ab < 0)

{

r = ab * -1;

}

else

{

r = ab;

}

cout << r << endl;

return;

}



int main()

{

for (size_t i = 0; i != 100; ++i)

cout << count_calls(i) << endl;

cout << factoral(4) << endl; fact(); absv(-3);

return 0;



}

以上是源碼檔(source file)以下是標頭檔Chapter6.h:

#pragma once

size_t count_calls(size_t i);

int factoral(int f) ;

void fact() ;

void absv(int ab);

3:00:00 如果讓Visual Studio 幫我們在標頭檔的函式宣告中自動在.cpp檔加上該宣告的定義的話,於.pcc檔Visual Studio會自動加上「#include "Chapter6.h"」此行。

3:2:00在Visual Studio 2019 方案總管中remove(移除)一個檔案,並不會真的在檔案總管中刪除那個檔案,只是在方案中移除而已;若須刪除檔案,須移至檔案總管手動刪除。

2:47:00

6.1.3. Separate Compilation

2:56:00

To allow programs to be written in logical parts, C++ supports what is commonly known as separate compilation .

要讓程式能以符合邏輯的方式撰寫,C++支援常被稱為個別編譯



2:57:30

Compiling and Linking Multiple Source Files

編譯與連結多個源碼檔

頁208

3:16:00

目的碼(object code)

這裡object不是物件或變數?



練習6.9

6.2. Argument Passing

6.2引數傳遞

3:29:40

3:31:50

As with any other variable, the type of a parameter determines the interaction between the parameter and its argument.

由此可見引數argument和參數parameter決定不是同一個東西。

當參數是參考(reference)型別時,它就與它的引數繫結(bind)

If the parameter is a reference (§ 2.3.1 , p. 50 ), then the parameter is bound to its argument.

否則,就是由引數拷貝copy值過來:

Otherwise, the argument’s value is copied.

原來傳址與傳值的差別就在這——參數是參考型別否:

3:45:10

When a parameter is a reference, we say that its corresponding argument is “passed by reference” or that the function is “called by reference.”

只要參數是參考(reference)型別,就是以傳址方式來傳遞引數給參數。

passed by reference:以參考方式傳遞

called by reference:以參考方式調用(呼叫)

頁209

3:37:35

參考是別名,是它所參考的對象的別名alias

a reference parameter is an alias for the object to which it is bound; that is, the parameter is an alias for its corresponding argument.

而參考(reference)型別參數的參考對象object就是其相應的引數

傳值的方式(即copy傳遞引數的方式)引數和參數是各自獨立的物件

但傳址的方式,參數就是引數的影子、別名、全權的代理者,參數所做的一切,都會回過頭來影響、改變引數的一切。



傳值6.2.1. Passing Arguments by Value

3:46:30

傳值的方式不會連動,傳址會。



3:55:30

Pointer Parameters

指標(pointer)型別的參數

指標的表現和其他非參考(reference)型別的物件一樣

總之,能被copy拷貝的物件,與其拷貝出來的,一定是各自獨立的東西(物件)

指標也提供了我們對它所指的物件的間接存取(權),因此我們也可以藉由指標來對它所指的對象作操作與設定

總之不是直接存一物件,都叫間接存取,不管是指標(pointer)還是參考(reference)

4:5:10

the object to which the argument points will be 0 ,

該 引數 指向 的 那個 物件 會 變成 0



頁210

C語言習慣用指標參數去操作函式外的物件,而C++則用參考參數,不用指標。

4:14:06

練習6.10

4:29:00

void SwapTwoInt(int* ip1, int* ip2)

{

int i1 = *ip1;

*ip1 = *ip2;//這就是C語言偏好用指針存取外部物件的方式

*ip2 = i1;

return;

}



int main()

{

int i1 = 3,i2=4;//這就是被C語言偏好的用指針存取的外部物件

int *p1 = &i1, *p2 = &i2;

SwapTwoInt(p1, p2);

return 0;



}

4:29:51

傳址6.2.2. Passing Arguments by Reference

頁211

4:37:51

Using References to Avoid Copies

用參考來避免拷貝

用參考以避免拷貝

4:42:00

It can be inefficient to copy objects of large class types or large containers. Moreover, some class types (including the IO types) cannot be copied.

拷貝大型類別型別或大型容器的物件可能很沒效率。更有甚者,某些類別型別(包括IO型別) 是無法拷貝的。

Moreover,翻成「何況」更適當。乃知「更有甚者」與「何況」亦表哥原理 也。

4:46:00

Because strings can be long, we’d like to avoid copying them, so we’ll make our

parameters references.

因為string可能很長,我們希望避免拷貝它們,所以我們會讓參數是參考。

我們希望避免→所以我們會想避免去

所以我們會讓參數是參考→因此我們就得讓參數是參考型別。讓參數是參考型別,當我們想要對此string型別物件作操作時,才能藉由這樣的參考型別參數來代理,而不是直接去存取原來的string物件(參考(reference)與指標(pointer)都是對型別物件的間接存取)

4:46:50

只要不涉及更動原件的,就用const來限定,以避免誤動



4:48:50

Using Reference Parameters to Return Additional Information

使用參考參數來回傳額外資訊

4:59:30

一個函式未必只能傳回一個值,而參考參數則便於做這類事情:

Reference parameters let us effectively return multiple results. As

我們可以定義一個新來型別來儲存傳回的多重值:

We could define a new type that contains the position and the count.

頁212

5:14:00

5:18:14

練習6.11

int resetVal(int& r)

{

r = 0;

return r;

}



int main()

{

int i = 3;

resetVal(i);

return 0;



}



5:20:55

5:41:50

string resetStr(string& r)//是不是參考型別的參數好像沒差多少

{

r = "";

return r;

}



int main()

{

string s ="";

for (size_t i = 0; i < 60000000; i++)

{

s += 'a';

}

resetStr(s);

return 0;



}

string resetStr(string* r)//是不是參考型別的參數好像沒差多少

{

*r = "";

return *r;

}



int main()

{

string s ="";

for (size_t i = 0; i < 60000000; i++)

{

s += 'a';

}

resetStr(&s);

return 0;



}

5:48:29

練習6.12

void SwapTwoInt(int& ir1, int& ir2)

{

int j = ir1;

ir1 = ir2;

ir2 = j;

}

int main()

{

int i1 = 3, i2 = 4;

SwapTwoInt(i1, i2);

return 0;

}

練習6.13

5:57:19

void f(T) 傳值 T型別的參數,也不是指標

void f(T&) 傳址 參考型別的參數(參考到T型別)



練習6.14

例子像前string s="" 的那個例子

要對付大體積的對象時。而且希望要能夠改動引數時。就用參考型別之參數

不希望更動引數,就盡量不要用參考參數。否則也得用const來限定

練習6.15

6:3:00

6:8:17

6.2.3. const Parameters and Arguments

6:17:44這const只是修飾parameters,而非arguments。常值的參數和(所傳遞的)引數之間的關係 7:23:30



6:10:00

頂層的const

a top-level const is one that applies to the object itself:



在拷貝或指定時,頂層的const會被省略。對於參數也適用。

有頂層的const的參數,我們可以用常值或非常值的引數來初始化它

6:18:35

相對於const說plain

6:20:37

頁213

6:21:11函式的多載(重載(overload))只能在參數序列(參數清單)有大不同時才行:

In C++, we can define several different functions that have the same name. However, we can do so only if their parameter lists are sufficiently different.

6:25:14

Pointer or Reference Parameters and const

常值與初始化的一般規則:

We can initialize an object with a low-level const from a non const object but not vice versa,

我們可以用一個非 const物件來初始化具有低層const (low-level const)的物件,但不能反過來,

6:32:00 不能用字面值(literal)來初始化參考:

error: can't initialize a plain reference from a literal (§ 2.3.1 (p. 50) )

6:44:30

Use Reference to const When Possible

盡可能使用對const的參考(reference to const),而不是普通的參考(plain reference)



6:48:40



頁214

using a reference instead of a reference to const unduly limits the type of arguments that can be used with the function.

使用一個參考而非對const的參考會不當地限制能與該函式並用的引數型別。

如果用普通的參考(plain reference)作為參數,則所能傳遞的引數型別就很有限了

因為參考的初始式有許多的限制。

一個對常值的參考的參數,可藉由字面值,常值,非常值來初始化,而一個非常值的參考,卻只能由一個同樣是非常值的型別來初始化,不能由常值來初始化。因為普通參考(plain reference)就要是要改變其所參考物件之值。不可改變的常值如何作為它的參考的對象?是這個原理。



// i如果s的結尾有單一個句號,那麼s就是一個句子

這個「i」是原文「if」的殘餘——未刪乾淨之羨文。為本書藉原文為底再改作中文之跡證。



But that fix only propagates the error—callers of is_sentence could pass only non const string s.

但這種修補方式只會使得錯誤傳播出去:

使得錯誤傳播出去→有這樣的中文嗎?→讓情況更形惡化or讓情形更加惡化

或者「錯上再加錯」。

就只好一錯再錯了。

7:22:33

6.2.4. Array Parameters

6.2.4陣列參數 8:28:00

陣列影響函式對之操作的兩大因素:

1.陣列不能被拷貝

2.陣列自行轉成對其首元素的指針

Because we cannot copy an array, we cannot pass an array by value.

不能拷貝就不能以傳值(pass by value)的方式傳遞陣列型別的引數

Because arrays are converted to pointers, when we pass an array to a function, we are actually passing a pointer to the array’s first element.

7:29:59

頁215

Exercises Section 6.2.3

練習6.16

bool is_empty(const string& s) { return s.empty(); }

練習6.17

7:45:00

bool contain_capital(const string& s)//練習6.17

{

for (char i :s)

{

if (isupper(i))

{

return true;

}

}

return false;

}

int main()

{

string s = "acaa";

cout<< contain_capital(s)<<endl;

return 0;

}

void beLowercase(string& s) {//因為要改動引數值,不可能用const參考

string::iterator si = s.begin();

while (si!=s.end())

{

*si = tolower(*si);

++si;

}

}



int main()

{

string s = "acQa";

beLowercase(s);

cout<<s <<endl;

return 0;

}

8:6:20

練習6.18

bool compare(matrix& m1, matrix& m2); //練習6.18

vector<int>::iterator change_val(int i, vector<int>::iterator vi);

練習6.19

8:16:00

//(a)

calc(23.4, 55.1);//illegal,參數名稱雖可省略,但只有一個參數,就只能傳一個引數

//(b)

count("abcda", 'a');//legal

//(c)

calc(66);//legal,66與double可隱含轉型

//(d)

sum(vec.begin(), vec.end(), 3.8);//legal,3.8可轉型為int(truncated to 3)

8:22:20

練習6.20

8:30:40

頁216

要在陣列作為引數傳遞時,得知其大小(元素有多少)的方式有3:

Using a Marker to Specify the Extent of an Array

使用一個標記來指定一個陣列的範圍

8:33:25

Functions that deal with C-style strings stop processing the array when they see a null character:

8:40:30

Using the Standard Library Conventions

使用標準程式庫的慣例

頁217

Explicitly Passing a Size Parameter

8:52:00

別忘了sizeof運算子也可以算出陣列元素數(陣列大小)

sizeof(array)/sizeof(*array)

sizeof(array)是一個陣列所有元素加起來的大小(各個元素皆同一型別),故再除以陣列第一元素之型別大小(引用陣列預設皆為其第一元素的指標(pointer),解參考(dereference)後即其元素本身),再向其作sizeof運算,即得出此元素型別的大小

9:0:59

Array Parameters and const

9:3:18

The discussion in § 6.2.3 (p. 213) applies equally to pointers as to references. When a function does not need write access to the array elements, the array parameter should be a pointer to const (§ 2.4.2, p. 62). A parameter should be a plain pointer to a nonconst type only if the function needs to change element

9:12:00

The parentheses around &arr are necessary (§ 3.5.1, p. 114):

f(int &arr[10]) // error: declares arr as an array of references

f(int (&arr)[10]) // ok: arr is a reference to an array of ten ints

頁218

9:14:30

Because the size of an array is part of its type, it is safe to rely on the dimension in the body of the function.

因為陣列的大小是其型別的一部份,仰賴函式主體中的維度大小是安全的。

如何仰賴?又什麼是函式主體中的維度大小?

Passing a Multidimensional Array

傳入一個多維陣列

留言

熱門文章