Try   HackMD
tags: 大一程設 東華大學 東華大學資管系 基本程式概念 資管經驗分享

新手的殺手王牌與助教的扣分重點 - 函式function (二) call-by-value、call-by-reference 以及 Overloading function

前言

如何看 block,不再搞混變數範圍,區域變數與全域變數 有跟大家提到了其中一種參數傳遞方法 call-by-value,但是這個方法會在呼叫函式時配置與傳入參數不同的記憶體區塊,導致不能更改同一個位置的內容,但大多時候,我們是需要修改相同變數的,而在 C++ 中我們有兩種方法,一種稱為 call-by-reference,一種叫做 call-by-address。

call-by-address 會在教 pointer(指標) 的時候才談,這一篇筆記只會講 call-by-value 與 call-by-reference。

如果 call-by-reference 跟 call-by-address 學不會,那代表你沒長大,筆記跟上課要記熟,不會要問到懂。
Orange

call-by-value

call-by-value 我其實已經在 這裡 講得很清楚了,還有 錄影 講解給你們聽,就超連結到那邊去吧,就不佔這篇筆記的篇幅了~

function calling & side effect

在正式談論 call-by-reference 之前,我想再帶大家更細看 function 這個東西。
在一般宣告函式與呼叫函式時會有如下的長相,相信大家都已經熟悉了。

void fun(int x, int y){ //FP /* statements */ } int main(){ int a, b; fun(a, b); //AP return 0; }

這邊有一些專有名詞想要讓大家知道 =>

  • 宣告函式時所規定要傳入的參數,我們稱為 Formal Parameter (FP, 型式參數)
  • 而在呼叫函式時我們所傳入的參數稱為 Actual Parameter (AP, 實際參數)

請注意紅筆地方,務必明確知道我在說什麼

所以學過 call-by-value 我們可以知道,在呼叫 call-by-value 樣態的函式的時候,AP 跟 FP 會佔用不一樣的記憶體,假設我們在 main 中宣告的變數作為參數傳進一個 function 中,則該變數是在 main 宣告並被配置一塊記憶體,但呼叫函式時我們只把 AP 的值傳進 function 中,而 FP 會再另外配置新的記憶體,所以兩者所在的記憶體位置並不相同。

而這個 AP 與 FP 所操縱的記憶體位置不同的現象我們稱為 No Side Effect(沒有副作用)

所以 AP 與 FP 若操縱相同的記憶體位置,我們就稱為 Side Effect,call-by-reference 馬上就會談到這個現象。

但通常大家都記英文,沒人在講中文
Orange

call-by-reference

call-by-reference 是 C++ 獨有的參數傳遞方式,在 C 語言內並沒有。
call-by-reference 能夠讓設計師去操弄相同位置的變數,那要怎麼做到呢?我們來看個例子。

void swap(int &args1, int &args2){ int temp = args1; args1 = args2; args2 = temp; } int main(){ int a = 10, b = 5; swap(a, b); cout << a << "," << b << endl; return 0; }

可以看到在第一行我們 swap 函式的參數內容中,有別以往我們看到的參數宣告方式,我們加上了 "&" 這個符號。
而在宣告變數時加上 "&" 這個符號,我們稱這樣的變數為參考變數 (reference variable)

跟之前補充的取得變數位址的 "&" 是在不同的情境哦,一個是在宣告,一個是在陳述。
針對取址有疑問的話回去看之前的影片
Orange

參考變數 reference variable

接下來說明參考變數的運作與宣告方式:

int x; int &count = x;

這邊的運作方式是,我們替 x 這個變數取一個別名叫做 count。

就像綽號一樣,我的綽號是 Orange,本名是柳成彥,你用這兩個方式叫我,都是指我。而參考變數的運作方式就是如此。由此可知,x 的參考變數 count,會去操控與 x 相同一塊記憶體位址。

大家先藉由看這個案例想想看,在心中決定一個答案,再把這個案例跑跑看,看是不是 count 真的會改變 x 的值。是不是與你心中決定的答案相同。

int main(){ int x = 5; int &count = x; count++; cout << x; return 0; }

如果參考變數你確定懂了,就可以繼續往下看 call-by-reference 了。

call-by-reference 的運作

先把剛剛的範例再貼上一次,如下。

void swap(int &args1, int &args2){ int temp = args1; args1 = args2; args2 = temp; } int main(){ int a = 10, b = 5; swap(a, b); cout << a << "," << b << endl; return 0; }

第一行內,藉由剛剛的說明我們可以瞭解 args1 與 args2 各為一個參考變數,所以我們在 main 呼叫 swap 的時候,傳入的 a 跟 b 分別被取了別名為 args1 跟 args2,像這樣。

int &args1 = a; int &args2 = b;

所以 args1 會操控 a 的那塊記憶體,args2 會操控 b 的那塊記憶體。
既然都操縱同樣一塊記憶體,在這個函式結束回到 main 時,a 跟 b 的值自然就會互換,因為 a 跟 b 的記憶體是在 main 內宣告,main 還沒結束,記憶體還沒釋放,所以做更改時會確實的交換兩者的值。

若你還是不信,就用取址運算子去印看看這些變數的記憶體位址吧,看看 a 跟 args1 的記憶體位址是不是相同,b 跟 args2 也同理。

還記得我講解 call-by-value 的影片時所畫的記憶體配置圖嗎?
試試看 call-by-reference 你們能不能畫出跟我一樣的結果。
Orange

所以總結,call-by-reference 操控的 FP 與 AP 是相同塊的記憶體,故呼叫函式時對 FP 的操作會影響 AP,由此可知這種參數傳遞方式會產生 Side Effect。

到了這邊如果你還是不懂,或是想實際看我講解一次,影片一樣附給你們。

Overloading Function 函式多載

  • 快速地用一句話來說就是,宣告同名但參數不同的函式,以達到不同的效果。
  • 藉由接收的參數們之參數型別或數量的不同來達到共用函式名稱的效果。

看看上面兩句定義哪種你比較容易理解,但都是一樣的意思。

先舉個簡單的例子。

int add(int a, int b) { return a+b; } double add(double a, double b) { return a+b; } int add(int a, int b, int c) { return a+b+c; } int main(){ cout << add(5, 6) << endl //印出 11 << add(4.3, 5.1) << endl // 印出 9.4 << add(5, 7, 1) << endl; // 印出 13 return 0; }

根據上面的例子,我有三個實作加法的函式,但是每個的功能各不相同,如果都要取不同的名字,有時候有點麻煩,但是 C++ 提供我們取同名字但根據參數的不同來觸發不同效果的功能,這就是函式多載。

像第二個加法我們實作浮點數的加法,第三個實作三個整數的加法,所需的參數的數量、型別都不相同,在程式執行時只要我們有確實的定義好函式規範,電腦是會認得這些差異的。

小補充 (下學期會教,有興趣的同學歡迎先行 google)

而函式多載的概念雖然簡單,但卻極為重要,在程式設計師的求學生涯中,絕對跑不掉物件導向程式設計,而對建構子的宣告就是運用函式多載的概念。

甚至是難度更高的抽象類別或是介面,對於函式多載的要求有時又更廣泛。