追蹤

宗宗大學

喜歡關注科學、哲學、社會、政治、教育,未來想當一位抱著理想的科學家,期許社會能夠進步。

[筆記]介面與實作、運算子多載、左值右值、參數傳遞、回傳多值|C++


運算子多載如何執行?int a = 10; ++a—;這串程式碼為什麼會出錯?居然跟左值右值有關!左值右值真的是一左一右嗎?參數傳遞中的傳指標和傳參考差在哪邊?C++居然無法回傳多值,那我該怎麼回傳多值?

還是很困惑嗎?文章裡有答案喔~😎

一、介面 (interface)與實作 (implementation)分離

實務上,會建議介面和實作分離,一個粗糙的分法是.h檔為介面,.cpp檔為實作,但這樣的分法不完全正確。 介面是指使用者會使用到的東西,例如:public函數的標頭、class的標頭。而function的實作和private (member data)裡的東西,都不屬於是介面,因為使用者不會直接使用到這些東西。


因此通常#include進來的檔案是介面,檔案中不會看太多實作的細節。這樣有些好處:第一、使用者不用知道如何實作,只要學會如何使用介面,即可使用這些功能,所以套件也是介面與實作分開的例子。第二、如果套件的更新只更新實作部分,而不更動到介面部分,這樣使用者的程式碼不需要變動,也能更新套件的功能。而如果套件的介面與實作是合在一起的,每次套件更新,使用者就可能要再更新自己的程式碼,這樣的套件是不會有人想用的。

介面與實作分開並不會影響到編譯器 (complier)的運作,因為編譯器只需要知道函式名、回傳值、參數列,這也剛好是函式的標頭 (header)內容。只有linker or loader (OS的程式,loader也可以包含linker的工作)才需知道實作內容。

至於哪些東西要放在介面?哪些要放在實作?基本上沒有一個有系統的規則,主要以經驗談。大方就是Public的介面更動相對難,Private的實作更動相對簡單。inline, explicit, default argument, virtual, override要放在.h中,.cpp不用放。

 二、函式 (Function)

運算子 (operator)、函式 (procedure)、函數 (function),本質上無太大的差別,只不過運算子可以使用運算子符號來呼叫函式,當然也可以用函式形式來呼叫函式;而函式 (procedure)是指無回傳值 (void)的;函數 (function)則是指有回傳值的。不過函式與函數的區分並沒有太大的意義,因此也常會混用。

(一)運算子多載 (Operator overloading)

函式多載有些前提:(1)需要在同一個scope內,不同scope就不是overloading。(2) 函式名相同,但簽章 (signature)不同。因此對於C++來說,每個多載函式都是獨立的函式,他們都是不同的函式,因為各自有獨特的簽章。函式簽章通常包含:函式名、參數型態、參數排序。

然後也不是所有的程式語言都支援運算子多載,C++有支援,但JAVA不支援,所以在JAVA中必須用函式多載。運算子多載如何運作?以輸入為例:cout << int << double << string;

 

1. 先overload operator 

cout << int == cout.operator<<(int),這會回傳cout (ostream&),使得可以連續輸出。cout << 之所以可以輸入不同型態的變數,是因為<<為一個多載運算子。

2. 決定優先順序:

(1) Precedence (優先順序)

(2) Associativity (結合性,左右順序)

Cout << int << double << string; == ( ( (Cout << int) << double) << string);

a<b<c必須拆成a<b && b<c,否則會有問題,因為這攸關結合性問題。a<b<c == ((a<b)<c),如果(a<b)為真,即會回傳1,因此((a<b)<c) == (1<c),顯然這不是我們要的結果。

&& 和 || 這兩個operator如果要overloading必須小心,e1 && e2 && e3 && e4……,原本如果e1為false,判斷就會結束,但如果overloading,此性質就會消失。

更多運算子優先順序的例子可以看期中考第8題

(二)左值 (lvalue)與右值 (rvalue)

左值與右值明顯的議題會出現在賦值 (assignment)這一動作,如:a = 1、b = c。首先何謂左值?何謂右值?左值通常是等號左邊的那個變數,也就是a和b都是左值,變數的本質是有名有記憶體位置的,而左值必須為變數,因此左值一定是有名字的;右值通常是等號右邊的數,也就是1和c都是右值,1本身是一個樣板數字 (literal number),是一個暫時且沒有名字的東西,因此右值可以是沒有名字的東西。因此一個簡單的判斷左右值的方法即為,左值需有名,右值可無名

有些運算子是涉及到賦值,如:=, ++, --, +=, -=, .......。++a == a = a + 1、—b == b = b - 1。以下有一個涉及到左右值的運算子問題:

int a = 10; ++a—;  // == (++(a—))

請問上面的a值為何?9?10?11?錯誤?答案是Compiling error。 因為++a需要一個rvalue,因為++a內含一個assignment,但a--會給lvalue

然後++, —盡量不要和macro(文取代)一起使用,因為Macro是copy整串的指令,容易會有意外的side effect,而不是copy計算值,所以指令會在copy後才執行運算。

更多左值右值的例子可以看期中考第6題

(三)參數傳遞

C語言的參數傳遞都是call-by-value,而pointer可以call-by-address。C++的call-by-reference和C的call-by-address很像,只不過C中的& (get address) and * (dereference)在C++中,編譯器自動幫你做。


call-by-value的缺點就是效率不好,因為它是用複製的方式在傳參數,但優點就是原本的參數不會因為函數內對引數做任何改變,而有所影響。call-by-reference的效率就會比較好,因為它是傳參考,參考可以視為變數的別名,因此只要改變引數,就會改變原本的變數。有時為了追求效能,但要沒有改變資料,就可以使用const reference 。

 

(四)回傳多值

C/C++只能回傳一個值,無法回傳多值。所以可能的方法有:
  1. 回傳指標(如:陣列):蠻危險的,因為要避免指到區域變數,或是被區域指標指到,區域變數或指標皆會因為block結束,而跟著被回收,此時的指標會指向空。
  2. 回傳struct / class:要先自己建立struct / class,將多個變數包在struct / class,struct / object可用call-by-value傳出去。
  3. reference:比較安全,但如果回傳值太多,就可能會需要有多個變數,或是用陣列/物件承載多個值。

本文章發表於:課程

加入85

宗宗大學

國立中山大學 生物科學系

追蹤 384 鼓勵作者

喜歡關注科學、哲學、社會、政治、教育,未來想當一位抱著理想的科學家,期許社會能夠進步。

鼓勵作者

目前持有 Blink Coin: Loading..

選擇禮物


愛心

(Coin 10)

幫高調

(Coin 20)

咖啡

(Coin 30)

掌聲鼓勵

(Coin 40)

崇拜眼神

(Coin 50)

驚呆了

(Coin 60)

神人4ni

(Coin 70)

花束

(Coin 100)

鑽石

(Coin 300)

紅寶石

(Coin 500)

藍寶石

(Coin 1000)

黃寶石

(Coin 3000)


送出鼓勵



發表匿名文章不會出現你的大頭圖與名稱,你可暢所欲言,但文章內容務必遵守「佈告欄使用規範」!


回應

送出回應


想回應這篇文章嗎?也想發表文章嗎?
馬上登入來發表文章、追蹤作者、收藏文章或回應文章吧!

註冊 登入