C++ 進階程式設計 Day2 === ###### tags: `c++` ### Recall - `auto` 作為函式宣告可以視為 `template` ```cpp= void f(auto a){ auto b = 7; // 語法角度幫忙推論 std::cout << a << std::endl; } int main(){ f(7); // 字面常數 可以推論為整數型態 } ``` - 等同 ```cpp= template<typename T> void f(T a){ T b = 7; // 語法角度幫忙推論 std::cout << a << std::endl; } int main(){ f(7); // 字面常數 可以推論為整數型態 } ``` #### 資工實作一遍會比別人還深入 算法的優缺點 - 範例學習是起點,背後動機是什麼才會用 - 他是指用新工具的哪個文件的函式 - 函式多載 - [sort 文件](https://en.cppreference.com/w/cpp/algorithm/sort) 知道範例是用哪個例子嗎?深入瞭解 - 以下是 編譯錯誤?實作定義?未定義行為? - 實作定義:編譯器定義 ```cpp= int main(){ int i; cout << i << endl; return 0; } // 未定義行為 ``` - 那以下是? ```cpp= int i; int main(){ cout << i << endl; return 0; } // 全域變數在編譯時候必須被算出 所以會是0 ``` - Question ```cpp= int main(){ double a = 1000000000000; int b = a; cout << b << endl; return 0; // 實作定義 ,塞不下會選擇long long... } ``` ## 賦值運算 - C語言預設:複製內容值 - 賦值運算子 `=` 優先順序是由右到左 ```cpp= #define a 3 // 取代a 為3 int main(){ int b[a]; } ``` #### `constexpr` - `constexpr` 表示值或傳回值為 constant,而且可能的話,會在編譯時期計算。 ```cpp= f(){ return 3; } int main(){ const int b = f(); } // 編譯器一開始會知道b嗎? 難道編譯期要去呼叫 f()? // 會導致編譯器工作量過大, constexpr,或是MACRO // 可以保證在編譯時期計算 ``` ```cpp= constexpr f(){ return 3; } int main(){ const int b = f(); } ``` ### Question 3 > 2 > 1 ? 1. `3 > 2 True` 2. `True > 1 ` ? `bool` 可以跟 `int` 比嗎? 3. 為了跟 `C` 相容,會存在隱性轉型 - `true` -> `1` - `false` -> `0` ### Q: 編譯時期一定要確保的事情有 1. 型態,陣列大小就是型態 2. 如果要讓編譯器知道,可以做很多事such as `MACRO`, `meta programming` ### 短路求值 ```cpp= int main(){ int a = 0; if( a!=0 && 3/a==0){ cout << "Hello" << endl; } } //執行 a!=0 就結束 ``` ```cpp= int main(){ int a = 0; if(3/a==0 && a!=0){ cout << "Hello" << endl; } } // 3/a==0 未定義行為 ``` ## 複合資料型態 ### Pointer - 儲存「記憶體位置」的資料型態 - `&a` “產生”暫時物件的記憶體位置 ### 指標相關運算 - (T*): 儲存記憶體位置的資料型態 - & :取得記憶體起始位置 - (*) :取得記憶體上的起始物件 ### 無法取得位置 1. 字面常數 ```cpp= //編譯錯誤 int main(){ int *b = &3; return 0; // 編譯器思維:如果字面常數跑的時候都可以取得位置。 // lvalue 才可以取值,lvalue未必是變數 } ``` - `lvalue`記憶體空間可以被賦值 2. 暫時物件 ```cpp= //編譯錯誤 int main(){ int a = 1; int *b = &(a + 1); return 0; } ``` - _**HINT**_:`C++` 編譯器世界難寫的,只有那幾套。 ### &&a 1. &a 是一個暫時物件。 2. &暫時物件,編譯錯誤 ### 參考概念,產生新的物件還是不要產生新的物件 - _**Ans**_ 不產生新的物件 ```cpp= int main(){ int a = 1; int &b = a; // b 就是 a 的別名 b = 5; cout << a << endl cout << b << endl; return 0; // ** 對 b 操作就是對 a 操作 } ``` ### 不存在參考的參考 &&a ### 陣列元素不能是參考 ### C++ 開了恐怖的窗 `rvalue` 可以被 `const T&`參考 ```cpp= int main(){ const int& a = 1; const int& b = a + 1; cout << a << endl cout << b << endl; return 0; // 唯獨的參考最為廣泛使用 } ``` - ? 唯獨的參考最為廣泛使用? 後面自定義類別,範型需要 ### 5 - 80 ```cpp= int main(){ int a = 3; const double& b = a; // 隱性轉型成暫時物件 b const long long& c = a;// 隱性轉型成暫時物件 c return 0; } ``` - `const T&` 可以參考暫時物件 ## 陣列型態 - `int var[3]` 一個父物件,三個子物件 ### 初始化的方式 ```cpp= int main(){ int a[3] = {1, 2, 3}; int b[3]{1, 2, 3}; return 0; } ``` ### 對陣列使用的範圍 ```cpp= int main(){ int a[3] = {1, 2, 3}; int b[3]{1, 2, 3}; for(auto v:b){ cout << v<< endl; // 這裡是做複製 } for(auto& v:b){ v = 0; // 全部被設定 cout << v<< endl; } return 0; } ``` ### 陣列不可以直接複製 C語言規定 ```cpp= int main(){ int a[3] = {1, 2, 3}; int *b = a; //隱性轉型成第一個陣列的記憶體位置 // int *b = &a[0]; for(int i = 0; i!=3; i++){ cout << b[i] << endl; cout << *(b+i) << endl; } } ``` - **HINT** `int *b = a`隱性轉型成第一個陣列的記憶體位置 ## 函式的宣告定義 - 有宣告沒有定義,連結錯誤 - 有定義沒有宣告,編譯錯誤 ### 函式呼叫,函式參數以引述進行初始化 ```cpp= void Print(const char*); int main(){ Print("Hello"); return 0; } void Print(const char* str){ cout << str << endl; } ``` ### Function Overloading ```cpp= void Print(const char*); void Print(char*) int main(){ Print("Hello"); //編譯的時候就知道了 Print('\n'); //編譯的時候就知道了 return 0; } void Print(const char* str){ cout << str << endl; } void Print(char str){ cout << str << endl; } ``` - 依照傳的`argument`與`parameter`的相似性決定呼叫哪一個函式 - `Python`沒辦法保證編譯的時候知道所有事 ### 傳遞物件到函式內 -> C++ 參考 ```cpp= void Swap(int&, int&); int main(){ int a = 1; int b = 2; Swap(a,b); } void Swap(int& a, int& b){ int t = a; a = b; b = t; } ``` ### 函式的回傳值是暫時物件 ```cpp= int f(){ return 1; } int main(){ f() = 5; }; // 編譯錯誤 暫時物件不能被賦予 ``` ```cpp= int& f(){ return 1; } int main(){ f() = 5; }; // 編譯錯誤 規則:字面常數不能被參考 ``` ```cpp= int f(){ int a = 1 return a; } int main(){ f() = 5; }; // 編譯錯誤 生命週期 ``` ```cpp= int& f(int& a){ return a; } int main(){ int a = 1; f(a) = 5; }; // 編譯過 參考到`main` 裡面的 a ,回傳原本`main` a ```