--- tags: books, note --- # Advanced function ## Function overload :::info 又稱函式多載、函式重載、函式超載 ::: 即使是用同樣的函數名稱,有的時候會呼叫到的是不同的函式 e.g. ```cpp= float f = 1.5; auto result = round(f); cout << typeid(decltype(result)).name() << endl; ``` ```result: f``` ```cpp= double f = 1.5; auto result = round(f); cout << typeid(decltype(result)).name() << endl; ``` ```result: d``` ```cpp= long double f = 1.5; auto result = round(f); cout << typeid(decltype(result)).name() << endl; ``` ```result: e``` 由上述例子我們可以知道即使是同一個函式名稱,內建函式round還是有至少三種版本 1. ```float round(float value);``` 2. ```double round(double value);``` 3. ```long double round(long double value);``` 那要多載一個函數也很簡單,只要確定函數的參數不一樣就可以讓多個版本同時存在 ```cpp= int add(int a, int b) { return a + b; } int add(int* a, int* b) { return *a + *b; } int main() { int a = 3, b = 5; cout << "add(int, int) " << add(a, b) << endl; cout << "add(int*, int*) " << add(&a, &b) << endl; return 0; } ``` ``` add(int, int) 8 add(int*, int*) 8 ``` 除了參數型態不一樣,參數數量不一樣也可以 ```cpp= int add(int a, int b) { return a + b; } int add(int a, int b, int c) { return a + b + c; } int main() { int a = 3, b = 5, c = 7; cout << "add(int, int) " << add(a, b) << endl; cout << "add(int, int, int) " << add(a, b, c) << endl; return 0; } ``` ``` add(int, int) 8 add(int, int, int) 15 ``` --- ## Operator overload :::info 又稱運算子多載、運算子重載 ::: 在開始前我們先來了解一下運算子的呼叫方式 像如果要做一個字串串接,我們應該會用這種形式 e.g. ```cpp= string a = "123", b = "456"; cout << a + b << endl; ``` ```result: 123456``` 那其實我們也可以用類似函式呼叫的方式來進行 e.g. ```cpp= cout << operator+(a, b) << endl; ``` ```result: 123456``` 除了一般的函式可以多載外,運算子某種意義來說也算是函式的一種 所以**運算子大部分都可以多載** :::info 這裡舉出一些不能被多載的運算子 ```::``` 範疇解析運算子 ```.``` 成員存取運算子 ```dynamic_cast``` 執行時檢查類型轉換運算子(?) ```static_cast``` 未經檢查的類型轉換運算子(?) ```reinterpret_cast```重定義類型轉換運算子(?) ```const_cast``` 更改非常數屬性運算子(?) ```typeid```取得類型資訊運算子(?) ```sizeof```取大小運算子(?) ```.*``` 成員物件選擇運算子(?) ```c?t:f```三元條件運算子 ```throw```丟擲異常運算子 ::: 多載的方法就跟函式多載基本上一樣,不過你不能覆蓋已經存在的運算子 :::warning e.g. ```cpp= int operator+(int a, int b){ return a + b + 10; } ``` 因為`int`跟`int`的加法運算已經被定義過了,所以就會編譯錯誤 ::: :::danger error: 'int operator+(int, int)' must have an argument of class or enumerated type ::: 因此需要有至少一個引數是自訂`class`或`enum` ```cpp= struct itype {int x;}; itype operator+(itype a, itype b) { return {a.x + b.x + 10}; } int main() { cout << (itype{5} + itype{6}).x << endl; return 0; } ``` ```result: 21``` 因為幾乎每個運算子都有固定的引數數量,所以多載的時候要注意引數有幾個 :::warning e.g. ```cpp= struct itype {int x;}; itype operator+(itype a, itype b, itype c) { return {a.x + b.x + c.x + 10}; } ``` ::: :::danger error: 'itype operator+(itype, itype, itype)' must take either one or two arguments ::: ### 習慣用法 可以透過多載`operator<`來讓自訂類別可以`sort` ```cpp= #include<iostream> #include<algorithm> #include<vector> using namespace std; struct two_element { int first, second; }; bool operator<(two_element a, two_element b) { if (a.first != b.first) return a.first > b.first; else return a.second > b.second; } int main() { vector<two_element> v{{2, 2}, {3, 1}, {2, 1}}; sort(v.begin(), v.end()); for (const auto& i : v) cout << i.first << ' ' << i.second << endl; return 0; } ``` ``` result: 3 1 2 2 2 1 ``` --- ## Default Arguments :::info 又稱預設參數、預設引數 ::: 如果今天有個函數```add``` 需要進行兩個數的加法或三個數的加法 那我們可以用上面學過的 [Function overload](#Function-overload)來實現 i.e. ```cpp= int add(int a, int b){ return a + b; } int add(int a, int b, int c){ return a + b + c; } ``` 但是這樣類似的東西就寫了兩次,既慢又不好看 那接下來就要介紹其中一個解決辦法: Default Arguments 正如名字裡敘述的,這個解決辦法就是將引數設預設值 講明白一點就是讓不指定引數值時,有一個預設的值可以用 剛剛那個例子就可以這樣改 i.e. ```cpp= int add(int a, int b, int c = 0) { return a + b + c; } ``` 在第三個引數`c`沒有用到時讓它預設為 $0$ 就可以達到跟上面多載兩個`add`一樣的效果 另外,Default Arguments 使用上需要注意的地方 e.g. ```cpp= int add(int a, int b = 0, int c) { return a + b + c; } ``` :::danger error: default argument missing for parameter 3 of 'int add(int, int, int)' ::: 像是這樣,中間有預設引數但後面沒有就會導致編譯錯誤 所以要將第一個用 Default Arguments 後的所有引數都設 Defalut argement才合法 e.g. ```cpp= int add(int a, int b = 0, int c = 0) { return a + b + c; } ``` > [color=#4ee0b9][name=9th進階教學$] [time=Sat, May 16, 2020 11:56 AM]