---
# System prepended metadata

title: Lecture 4 Functions

---

# Lecture 4 Functions

函式在程式設計中扮演重要的角色，撰寫程式時通常會將重複使用到的運算包裝成函式。函式原型（Function prototype）定義了函式的外觀，其中包含回傳值、函式名稱及傳入參數，三者要素。函式原型通常會定義在獨立的標頭檔，並以另一個 .cpp file 實作函式中的主體運算。而值得注意的是若函式是在 main 之後實作，必須在 main 之前進行宣告，否則會出現編譯錯誤。

Parameters and Passing Arguments
---
當在設計函式時，傳入的參數大致可分為3類：
* 傳值或傳址
* 傳入常數地址（const reference parameters）
* 傳入指標或陣列

傳址是應用在需要被函式改變的任何物件。
傳值是應用於不想被函數改變的小物件。
傳入常數地址則適用在不想被函數改變的大型物件，如 vector、string、自訂義的類別等。
```
#include <iostream>
#include <string>
using namespace std;
// returns the index of the first occurrence of c in s
// the reference parameter occurs counts how often c occurs
string::size_type find_char(const string &s, const char &c, string::size_type& occurs){
    auto ret = s.size(); // position of the first occurrence, if any
    occurs = 0; // set the occurrence count parameter
    for (decltype(ret) i = 0; i != s.size(); ++i) {
        if (s[i] == c) {
            if (ret == s.size())
                ret = i; // remember the first occurrence of c
            ++occurs; // increment the occurrence count
        }
    }
    return ret; // count is recorded in the occurs
}
int main() {
    string s = "Hello world";
    string::size_type cnt;
    auto index = find_char(s, 'o', cnt);
    cout << "Character o first occurs at the index of "
    << index << " in " << s << endl;
    cout << "Character o occurs " << cnt << " times"
    << " in " << s << endl;
    return 0;
}
```

#### Array Parameters
使用陣列作為參數的宣告方式為Type name[]，而作為陣列變數本身也是指向陣列中第一個元素的指標故亦可寫作Type* name。
```
void function(int a[])// void function(int *a)
```
而當呼叫函式時，參數和引數間是透過執行附值的動作將引數引入函式中進行計算。因此當某個函式使用陣列作為參數，在函式中 a 雖然指向傳入的陣列位置但卻非原本的陣列，因此在函式內使用 begin()、end() 等使用到陣列長度的方法將會失去原本的意義。
```
// using begin() & end() inside the fuction
int sum(int a[]){
    int summation = 0;
    for (auto i=begin(a); i!=end(a); ++i)
        summation += i;
    return summation;
}// cannot use begin and end in the function since the size of the array is lost in the function.

// using for range inside the fuction
int sum(int a[]){
    int summation = 0;
    for (int i:a)
        summation += i;
    return summation;
}// when passing the array into a function, the range-for WILL NOT work since we lose an important information: the size of an array!
```
解決該問題的一種方法是傳遞陣列的參考而非陣列。
```
#include <iostream>
using namespace std;
// passing array reference to function with fixed size 5
void arrAutoTest(int (&ia)[5]) {
    for (auto i : ia)
        cout << i << " ";
}
int main() {
    const size_t array_size = 5;
    int ia[array_size] = { 0, 1, 2, 3, 4 };
    arrAutoTest(ia);
    return 0;
}
```
但上述的方法會綁定陣列的大小，而使函數不便於操作，更進階的方法是使用函式樣版。
```
#include <iostream>
using namespace std;
// passing array reference to function with generic size arr_size
template <std::size_t arr_size>
void arrAutoTest(int(&ia)[arr_size]) {
    for (auto i : ia)
        cout << i << " ";
}
int main() {
    const size_t array_size1 = 7;
    int ia[array_size1] = { 0, 1, 2, 3, 4, 5, 6 };
    arrAutoTest(ia);
    cout << endl;
    const size_t array_size2 = 4;
    int ib[array_size2] = { 0, 1, 2, 3 };
    arrAutoTest(ib);
    return 0;
}
```

Return Types
---
在定義函式時，必須定義傳回值型態，如果函式不傳回值，使用 void 表示不傳回任何數值；若定義了傳回值型態不為 void，函式最後要使用 return 傳回數值，否則編譯器失敗。
* 回傳非參考物件 & 回傳參考物件（千萬不能回傳在函式內建立的 local object）
```
// return plural version of word if ctr isn't 1
string make_plural(size_t ctr, const string& word, const string &ending){
    return (ctr == 1) ? word : word + ending;
}

// find longer of two strings
//  lvalue 參考
const string& shorterString(const string &s1, const string &s2){
    return s1.size() < s2.size() ? s1 : s2;
}
```
定義傳回值型態為 rvalue 參考，通常是為了借此避免不必要的複製，藉而提升效率。
```
// rvalue 參考
#include <iostream> 
using namespace std; 

string&& concat(string &&lhs, string &rhs) {
    lhs += rhs;
    return std::move(lhs);
}

int main() { 
    string s = "++";
    string result = concat("C", s);
    cout << result << endl;

    return 0; 
} 
```
* 回傳指標
代表著記憶體位址在函式執行完後，必須仍是有效的，通常代表著函式內會配置動態記憶體，再將其回傳。
```
int* makeArray(int m, int initial = 0) { 
    int *a = new int[m]; 
    for(int i = 0; i < m; i++) {
        a[i] = initial; 
    }
    return a;
}
```

Overloaded Functions
---
C++支援函式重載（Overload），為類似功能的函式提供了統一名稱，然而根據參數個數或型態的不同，由編譯器選擇要呼叫的函式，函式重載令開發者在設計函式名稱可以簡便一些。
Q：Given
`int gcd(int v1, int v2);`
Are the following overloaded functions valid?
```
int gcd(double v1, double v2);
int gcd(int v1, int v2, int v3);
int gcd(int v1, int v3);
int gcd(double v1, int v3);
double gcd(int v1, int v3);
```
A：
Y, Y, N, Y, N

Functions with Default Arguments
---
在函式中使用預設引數時，在實際操作函式時就無需再輸入該引數。在設計時應注意，預設引數需要放在位預設的參數之後。除此之外，操作函式時輸入的引數會依照順序對應參數。
```
int f(int, int=0, int=0); //ok
int f(int=0, int=0, int); //error
int f(int=0, int, int=0); //error
int f(int, int, int=0); //ok
```
