# C++程式教學 #### By Ching-Kai Tseng (CKK) --- 為了方便大家閱讀,可以請大家先取得這份簡報,裡面有各種程式碼提供參考 * https://reurl.cc/9vk48a * ![qrcode](https://hackmd.io/_uploads/Bk97a45IC.png) **P.S 希望大家別先看進度後面的內容,這樣會暴雷 : (** --- ## 大家都加入slido一起討論吧! * https://reurl.cc/4r95ER * ![image](https://hackmd.io/_uploads/SkQRU4xwR.png) * 想做什麼都可以,只要不涉及**人身攻擊**等行為 --- ## 簡報服用方式: * 方向鍵很重要! ![image](https://hackmd.io/_uploads/BJCKSH9UR.png) ---- * 向右:切換到下一章節 * 向左:切換到上一章節 * 向下:下一頁投影片 * 向上:上一頁投影片 * P.S 如果沒辦法往下了,代表這個章節已經結束,可以向右到下一章節 * **手機板**也可以用滑動的方式換頁哦 ~ --- ## 首先,我們先來認識一下C++吧 ![image](https://hackmd.io/_uploads/ryUhdDCSR.png) ---- ## C++歷史 * 1980年代由*比雅尼·斯特勞斯特魯普*發明 * 1998年,國際標準組織頒布了C++程式設計語言的第一個國際標準ISO/IEC 14882:1998 * 目前最新標準為ISO/IEC 14882:2020(包含包含了核心語言和標準庫的規則) 更多資訊請右轉[維基百科](https://zh.wikipedia.org/zh-tw/C%2B%2B) ---- <font size=3>https://hellogithub.com/report/tiobe/</font> ![image](https://hackmd.io/_uploads/S1OVANlvA.png) ---- ## C++有什麼特性? 1. 編譯語言(全部轉換為binary執行檔) 2. 標準模板庫**STL**(各種神奇容器vector, list, map, set以及算法sort, find等) 3. 異常處理(try, throw, catch) 4. inline function(以空間換取時間!) 5. <font color="red">物件導向(Object Oriented Programming)</font> ---- 一個簡單的物件... ```cpp= class Fraction { private: int numerator; // 分子 int denominator; // 分母 public: void reduce() { // 用來簡化分數的函式(約分) int gcdValue = gcd(numerator, denominator); // 取分子和分母的最大公因數 numerator /= gcdValue; denominator /= gcdValue; } int gcd(int a, int b) { // 用來算出最大公因數的function while (b != 0) { int t = b; b = a % b; a = t; } return a; } int lcm(int a, int b){ // 用來計算最小公倍數的function return (a / gcd(a, b)) * b; } // Implementation function Fraction(int num, int denom) { numerator = num; denominator = denom; if (denominator == 0) { // 如果分母 = 0 時 throw invalid_argument("Denominator cannot be zero"); // throw 出一個 Denominator cannot be zero 的錯誤訊息 } reduce(); // 對輸入的分數約分 } Fraction add(const Fraction& other){ // 與另一個分數相加 int commonDenominator = lcm(denominator, other.denominator); // 求通分的分母(用最小公倍數) int num1 = numerator * (commonDenominator / denominator); int num2 = other.numerator * (commonDenominator / other.denominator); int newNumerator = num1 + num2; return Fraction(newNumerator, commonDenominator); // 回傳相加之後的分數 } Fraction subtract(const Fraction& other){ // 與另一個分數相減 int commonDenominator = lcm(denominator, other.denominator); // 求通分的分母(用最小公倍數) int num1 = numerator * (commonDenominator / denominator); int num2 = other.numerator * (commonDenominator / other.denominator); int newNumerator = num1 - num2; return Fraction(newNumerator, commonDenominator); // 回傳相減之後的分數 } Fraction multiply(const Fraction& other){ // 與另一個分數相乘 int newNumerator = numerator * other.numerator; // 分子相乘 int newDenominator = denominator * other.denominator; // 分母相乘 return Fraction(newNumerator, newDenominator); // 回傳相乘之後的分數 } Fraction divide(const Fraction& other){ if (other.numerator == 0) { // 如果除數(分數)是0 throw invalid_argument("Cannot divide by zero fraction"); // throw 出一個 Denominator cannot be zero fraction 的錯誤訊息 } // 進行分數相除的計算 int newNumerator = numerator * other.denominator; int newDenominator = denominator * other.numerator; return Fraction(newNumerator, newDenominator); // 回傳相除後的分數 } Fraction operator+(const Fraction&); Fraction operator-(const Fraction&); Fraction operator*(const Fraction&); Fraction operator/(const Fraction&); void print(){ // 將分數顯示出來 cout << numerator << "/" << denominator << endl; } }; ``` ---- #### Q. 看不懂剛剛的程式碼,學長可以下課了嗎啊啊啊ヾ(;゚;Д;゚;)ノ゙ ![image](https://hackmd.io/_uploads/H12TgOArA.png =x450) ---- ### A. 沒事啦,今天包你學會( • ̀ω•́ ) --- ## 我們先寫出第一個C++程式吧! ---- Hello World! ```cpp= #include<iostream> // 使用iostream函式庫 using namespace std; // 使用std命名空間 int main(){ // 定義主要function cout<<"Hello World!"<<endl; // 在電腦螢幕上印出引號內文字 return 0; // 結束程式並回傳代碼0 } ``` ---- Result: ![image](https://hackmd.io/_uploads/BJZ-VuRHA.png) --- ## C++ 運算子 ---- ### 算術運算子 | 加法 | 減法 | 乘法 | | :---: | :---: | :---: | | `a + b` | `a - b` | `a * b` | | 除法 | 取mod(餘數) | | | `a / b` | `a % b` | | ---- #### Example ```cpp= #include<iostream> using namespace std; int main(){ int a = 4; int b = 3; cout<<a + b<<endl; // 7 cout<<a - b<<endl; // 1 cout<<a * b<<endl; // 12 cout<<a / b<<endl; // 1 cout<<a % b<<endl; // 1 cout<<b % a<<endl; // 3 return 0; } ``` ---- #### C++的算術運算也是先乘除後加減(**取餘數與乘除在同一順位**) ```cpp= #include<iostream> using namespace std; int main(){ cout<<10 + 20 * 3<<endl; // 70 cout<<(10 + 20) * 3<<endl; // 90 return 0; } ``` ---- ### 關係運算子 | 等於 | 不等於 | 大於 | | :---: | :---: | :---: | | `a == b`| `a != b` | `a > b` | | 小於 | 大於等於 | 小於等於 | | `a < b` | `a >= b` | `a <= b` | ---- #### Example ```cpp= #include<iostream> using namespace std; int main(){ int a = 1; int b = 2; int c = 3; cout<<(a==1)<<endl; // true cout<<(a==b)<<endl; // false cout<<(a!=c)<<endl; // true cout<<(c>=b)<<endl; // true cout<<(a<=1)<<endl; // true } ``` ---- ### 邏輯運算子 | 且 | 或 | 非 | 邏輯互斥 | | :---: | :---: | :---: | :---: | | `a && b` | `a \|\| b` | `!a` | `a^b` | ---- #### Example ```cpp= #include<iostream> using namespace std; int main(){ bool a = true; bool b = false; cout<<a<<endl; // 1 cout<<!a<<endl; // 0 cout<<(a&&b)<<endl; // 0 cout<<(a||b)<<endl; // 1 cout<<(a&&(!b))<<endl; // 1 cout<<(a^(!b))<<endl; // 0 } ``` --- ## C++ 基本變數型態、宣告 ---- ### 變數宣告方式 `<變數類型> <變數名稱> [ = 值];` ---- ### 命名變數需要注意的是... * 變數必定是**英數字和底線**的組合 * 名稱的第一個字元**不能是數字** * 避免與C++內鍵變數名或函式名重複(int, char, string, max 等) * **駝峰**命名法、**底線**命名法 ---- example ```cpp= int firstVar = 2024; int secondVar = 113; int thirdVar; ``` ---- ### 變數的作用範圍 1. 全域變數:可以讓所有執行程式碼都取得的到 2. 區域變數:在那一個函式裡面才能取得 #### 注意:<font color="red">不要濫用全域變數</font>,不然可能會產生衝突 ---- ```cpp= #include<iostream> using namespace std; int global_var = 10; // 全域變數(寫在最外面) void func1(){ int local_var = 2; cout<<local_var<<endl; // 2 cout<<global_var<<endl; // 10 } int main(){ int local_var = 1; cout<<local_var<<endl; // 1 cout<<global_var<<endl; // 10 func1(); } ``` ---- #### 變數是可以被更改的 ```cpp= #include <iostream> using namespace std; int main() { int a = 10; cout<<"a = "<<a<<endl; a = 20; cout<<"a = "<<a<<endl; return 123; } ``` ---- Output: ![image](https://hackmd.io/_uploads/BykdT_CrR.png) ---- #### 相對的,常數(const)是不能更改的 ```cpp= #include <iostream> using namespace std; int main() { const int a = 10; cout<<"a = "<<a<<endl; a = 20; cout<<"a = "<<a<<endl; return 123; } ``` ![image](https://hackmd.io/_uploads/B1JSNB9IR.png) ---- ### int ```cpp= int myGrade = 59; int yourGrade = 100; ``` ---- ### int * 是一種儲存**整數**的資料型別 * 有4bytes的儲存空間 * 可儲存-2,147,483,648 至 2,147,483,647的整數(超過會很可怕) ---- ### int 運算子 1. '+' (`a+b`):回傳兩個int變數相加 2. '-' (`a-b`):回傳兩個int變數相減 3. '\*' (`a*b`):回傳兩個int變數相乘 4. '/' (`a/b`):回傳兩個變數相除 5. '%' (`a%b`):回傳a除以b的餘數 ---- #### Example ```cpp= int main(){ int a = 4; int b = 3; cout<<a + b<<endl; // 7 cout<<a - b<<endl; // 1 cout<<a * b<<endl; // 12 cout<<a / b<<endl; // 1 cout<<a % b<<endl; // 1 cout<<b % a<<endl; // 3 return 0; } ``` ---- ### double ```cpp= double d1 = 3.14159; double d2 = 2.71828; ``` ---- ### double * 是一種儲存**浮點數**的資料型別 * 有8bytes的儲存空間 * 可儲存1.7E-308 至 1.7E+308的浮點數 ---- ### double 運算子 * 與int大致相同 * double與int的相互計算時,儲存為int的變數會被強制轉型成double ---- ### char ```cpp= char c1 = 'A'; char c2 = 'a'; ``` ---- ### char * 是一種儲存**字元**的資料型別 * 有1byte的儲存空間 ---- https://www.ascii-code.com/ ![image](https://hackmd.io/_uploads/S1rZLDU8R.png) ---- ### char 運算子 * 將字元轉換成ascii code的編號再進行數學計算 * 計算完回傳int資料型態 ---- ```cpp= #include<iostream> using namespace std; int main(){ char c1 = 'A'; char c2 = ' '; cout<<"c1: "<<c1<<endl; // A cout<<"c2: "<<c2<<endl; // 空格 cout<<"(c1 + 1) = "<<(c1 + 1)<<endl; // 66 cout<<"(c1 + c2) = "<<(c1 + c2)<<endl; // 97 cout<<"('a'/2) = "<<('a'/2)<<endl; // 48 } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/char_calculate.cpp) ---- ### Output: ![結果會回傳數字](https://hackmd.io/_uploads/BJiMnev8A.png) #### Q. 那我們要如和輸出字元呢? ---- ### 強制轉型(coercion)   也就是將某種資料型態轉換為另一種的過程,在程式進行的過程中十分常見。 ---- ### 例如... ```cpp= #include<iostream> using namespace std; int main(){ int a1 = 12; double a2 = 3.55; cout<<(a1 + a2)<<endl; // 會將a1轉換成double型態再與a2計算 } ``` ---- ### 手動進行強制轉型 ```cpp= #include<iostream> using namespace std; int main(){ cout<<(char)65<<endl; // A } ``` ---- `(變數型態)<欲轉換的變數名稱 or 資料>` ```cpp= (char)65; ``` `變數型態(<欲轉換的變數名稱 or 資料>)` ```cpp= char(65); ``` --- ## C++ 判斷式 ```cpp= int main(){ int num = 7; if(num > 9){ cout<<"A"<<endl; }else if(num <= 9 && num >= 1){ cout<<"B"<<endl; }else{ cout<<"C"<<endl; } // OUTPUT: B } ``` ---- ### if - else ```cpp= if(判斷式或布林值){ 滿足條件時執行的動作 }else if(判斷式或布林值){ 滿足條件時執行的動作 }else if(判斷式或布林值) . . . }else{ 不滿足上述所有條件時執行的動作 } ``` ---- ### 巢狀 if ```cpp= #include <iostream> using namespace std; int main() { int a = 10; int b = 20; if (a > 5) { // 外層的 if-else if (b > 15) { // 內層的 if-else (滿足 a > 5 才會進來) cout << "a is bigger than 5 and b is bigger than 15" << endl; } else { cout << "a is bigger than 5 but b is not bigger than 15" << endl; } } else { cout << "a is not bigger than 5" << endl; } return 0; } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/nested_if.cpp) --- ## C++ 迴圈 1. **for** loop 2. **while** loop ---- ### for loop ```cpp= for(設定變數初始值; 設定變數的上(下)限; 每次執行完對變數做的動作){ 在迴圈中的程式碼 } ``` ---- ### Example ```cpp= #include<iostream> using namespace std; int main(){ for(int a=1; a<=8; a+=1){ cout<<"Number: "<<a<<endl; } } ``` ---- ### Output ![image](https://hackmd.io/_uploads/HJU1JeF8A.png) ---- ### while loop ```cpp= while(判斷式或布林值){ 迴圈內執行的程式碼 } // 先判斷再執行 ``` Or ```cpp= do{ 迴圈內執行的程式碼 }while(判斷式或布林值); // 先執行再判斷 ``` `若while內的判斷式值為false會跳出迴圈` ---- ### 這兩個有什麼差異?! 猜猜這個程式會輸出什麼 ```cpp= #include<iostream> using namespace std; int main(){ do{ cout<<"I hope I won't be executed"<<endl; }while(false); } ``` ##### 用各位電腦中的C++編輯器來揭曉答案吧 ---- 這個呢? ```cpp= #include<iostream> using namespace std; int main(){ while(false){ cout<<"I REALLY don't want to be executed"<<endl; } } ``` ---- ### break, continue 1. break可以用來跳出一個迴圈,不管迴圈條件是否滿足 2. continue用來讓電腦馬上執行下一次迴圈 ---- ### break ```cpp= #include<iostream> #include<time.h> using namespace std; int main(){ srand(time(nullptr)); // 使用當前的時間製作一個rand種子值 while(1){ int rd = rand()%10; //產生0到9之間的隨機數 cout<<rd<<endl; if(rd == 5){ break; // 如果隨機生成的數字 = 5時,跳出迴圈 } } } ``` ---- ### Output ![image](https://hackmd.io/_uploads/SkkxBlF80.png) ---- ### continue ```cpp= #include<iostream> using namespace std; int main(){ for(int a=1; a<=50; a++){ if(a%5 != 0){ // 如果 a 無法被5整除 continue; // 則跳到下一次迴圈 } cout<<a<<endl; } } ``` ---- ### Output ![image](https://hackmd.io/_uploads/rydVPxY80.png) --- ## 讓我們複習一下上次的教學內容 ---- ### 暖身 - 簡單迴圈 #### 還記得ascii code嗎? ![image](https://hackmd.io/_uploads/S1rZLDU8R.png =x400) ---- #### 用迴圈的方法把編號33 ~ 123的字元都顯示出來(用字元的方式,不要是數字) * 觀念:迴圈、強制轉型、cout * 字元之間換行或空格均可 * 1pt/group ---- ### if - else + 迴圈 #### 我們來做一個數字炸彈遊戲 1. 先讓電腦隨機生成一個數字 2. 不停重複讓使用者輸入數字 3. 當輸入的數字和生成的數字相同就輸了!!! #### 1pt/group ---- #### 從這邊來改吧 ```cpp= #include<iostream> #include<time.h> using namespace std; int main(){ srand(time(nullptr)); int randomNumber = rand()%100; // 隨機生成一個0 到 100之間的整數 while(true){ cout<<"Please input a number: "; int userInput; cin>>userInput; // 讓使用者輸入userInput這個數字用的程式碼 // 接下來靠你們了,要寫出判斷輸入的數字是否為生成數字並且跳出迴圈的程式碼 } // 輸出遊戲結束的文字 cout<<"Boom!!!!!"<<endl; cout<<"Game Over"<<endl; return 0; } ``` ---- ### 難度提升! #### 這個遊戲一定是多個人玩才好玩吧,那我們再加入一些東西,讓他變multiplayer game (這裡先兩個人) * 讓程式判斷現在玩的人是誰,並在螢幕上提示現在是輪到誰了 * 程式要知道是誰輸了,並且在螢幕上顯示 * 1pt/group ---- #### 從這邊來改吧 ```cpp= #include<iostream> #include<time.h> using namespace std; int main(){ int numberOfPlayers = 2; srand(time(nullptr)); int randomNumber = rand()%100; // 隨機生成一個0 到 100之間的整數 int nowPlaying = 0; // 用來儲存現在是第幾位玩家在玩 while(true){ cout<<"Player "<< " /*這邊要讓電腦顯示是第幾位玩家要輸入*/ " <<", Please input a number: "; int userInput; cin>>userInput; // 讓使用者輸入userInput這個數字用的程式碼 // 接下來靠你們了,要寫出判斷輸入的數字是否為生成數字並且跳出迴圈的程式碼 // 這邊可能要判斷是否要回到第一位玩家輸入 } // 輸出遊戲結束的文字 cout<<"Boom!!!!!"<<endl; cout<<"Player "<<nowPlaying + 1<<" lose the game."<<endl; // 記得 + 1,不然他會是從第0位玩家算起的 return 0; } ``` ---- ### 難度提升! #### 如果今天有很多人要玩怎麼辦?? * 加一個輸入讓使用者自己說要多少人 * 其他功能跟上一個程式一樣 * 提示:前一關都用if - else的同學可以試著用看看取餘數(參考就好) * 1pt/group ---- ### 範例輸入/輸出 ```shell= Input the number of players: 3 Player 1, Input a number: 1 . . . Player 2, Input a number: 3 Player 3, Input a number: 12 Boom!!!!! Player 3 lose! ``` ---- 這個我來現打程式碼吧! --- ## C++ 陣列 ![Segmentation fault](https://hackmd.io/_uploads/S1NOklJPC.png) ---- ### 陣列介紹 * 由多個同一型態的變數組合而成 * 分為靜態陣列及動態陣列(pointer) * 計數均**從0開始** * 注意:靜態陣列必須先**指定元素數量** ---- ### 如何新增一個陣列? 1. 指定資料型態 2. 設定陣列名稱(與變數命名限制相同) 3. 指定資料數量 4. Optional:寫入陣列內容 `資料型態 陣列名稱[數量]([更多維度]...);` ---- ### Example ```cpp= #include<iostream> using namespace std; int main(){ int arr1[10]; // 新增一個10項的陣列arr1 int arr2[10] = {1, 2, 3, 4, 5, 1, 2, 3, 4, 5}; // 新增一個10項的陣列arr並賦值 int two_dimension1[5][5]; // 新增一個 5x5 的陣列 int two_dimension2[5][5] = { // 新增一個 5x5 的陣列並賦值 {1, 2, 3, 4, 5}, {5, 4, 3, 2, 1} }; } ``` ---- ### 更改/取用陣列內的元素 ```cpp= #include<iostream> using namespace std; int main(){ int arr1[10]; for(int a = 0; a < 10; a++){ arr1[a] = a+1; } for(int a = 0; a < 10; a++){ cout<<arr1[a]<<", "; } cout<<endl; } ``` ---- ### 小練習 - 還是數字炸彈遊戲 * 之前的數字炸彈遊戲有一個問題,就是可以不斷輸入重複的數字 * 這裡使用陣列的方式,記錄所有已經輸入過的數字(**僅供參考**) * 當使用者輸入以輸入過的數字時,電腦要提示說已經輸入過,並讓使用者再輸入一次(不斷重複值到使用者輸入還未被輸入的數字) * 不只一種解法,嘗試用最快速的方法判斷數字是否被輸入過 * 3pts/group ---- ### 另一個小練習 #### 現在已經有很多破解魔術方塊的機器人,但你想要自己做一個。 #### Day 1 讓機器人辨識到現在魔術方塊各是什麼顏色 ---- #### 目標: * 製做一個用來儲存魔術方塊顏色的陣列(這裡以2x2x2為例)因此有6面,每面有2x2個方塊(寫個三維陣列?) * 讓使用者輸入目前的魔術方塊資料 * 輸出魔術方塊的第二面 * 我們用數字1~6代表顏色(或者也可以嘗試用英文字母看看) * 1pt/group ---- 範例輸入/輸出: ```shell= Please input the 1th plane of the cube: Row 1: Column 1: 1 Column 2: 2 Row 2: Column 1: 3 Column 2: 4 . . . Please input the 6th plane of the cube: Row 1: Column 1: 3 Column 2: 4 Row 2: Column 1: 5 Column 2: 6 The 2nd plane of cube is: 5, 6, 1, 2, ``` ---- ### 提示 ```cpp= #include<iostream> using namespace std; int main(){ int cube[6][2][2]; // 這裡可能要用到3層for 迴圈 cin>>cube[a][b][c]; // 這裡可能要用到3層for 迴圈 } cout<<"The 2nd plane of cube is: "<<endl; for(int a=0;a<2;a++){ for(int b=0;b<2;b++){ // 這邊要輸出什麼哩? } cout<<endl; } } ``` ---- ### answer [GitHub 連結](https://github.com/tseng91301/bime_camp_tutorial/blob/main/practice/array_practice1.cpp) ---- ### 警語: * 請隨時注意當前陣列的長度 * 不要存取超過範圍的陣列內容(最常見的錯誤是取10位陣列的第10項或行列看錯) ---- ### 一個簡單的錯誤示範... ```cpp= #include<iostream> using namespace std; int main(){ int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} }; for(int a=0;a<4;a++){ for(int b=0;b<3;b++){ cout<<matrix[a][b]<<" "; } cout<<endl; } } ``` ---- ### 希望讓陣列的大小是可以變化的? ```cpp= /*這個程式碼是6小隊輔「阿瑋」教我的,他是修過資工系DSA課程的大電神*/ #include<iostream> using namespace std; int main(){ int amount = 10; int *arr; arr = (int*)malloc(sizeof(int) * amount); // 指定arr這個指標存放10個int的記憶體大小 for(int a=0;a<amount;a++){ arr[a] = a; } cout<<"The origin array: "; for(int a=0;a<amount;a++){ cout<<arr[a]<<" "; } cout<<endl; amount += 5; arr = (int*)realloc(arr, sizeof(int) * amount); // 為arr重新分配15個int的記憶體大小 for(int a=10;a<amount;a++){ arr[a] = amount - a; } cout<<"The altered array: "; for(int a=0;a<amount;a++){ cout<<arr[a]<<" "; } cout<<endl; free(arr); } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/array_example3.cpp) ---- 當然,我們今天不會教這個,之後會教更簡單的工具,讓我們拭目以待( Φ ω Φ ) --- ## C++ function ```cpp= #include<iostream> using namespace std; int add_num(int i1, int i2){ int result = i1 + i2; return result; } int main(){ int add_res = add_num(100, 150); cout<<"add_res = "<<add_res<<endl; } ``` ---- ## C++ function 用法: ```cpp= 返回值類型 函式名稱([參數列表]){ 函式內的程式碼 return 回傳的值(若有需要) } ``` ---- ### 小練習:自己做一個function * 做一個函式,功能是計算直角三角形的斜邊高 * 需要有3個輸入,分別是兩個短邊和一個斜邊 * 經過函式的計算,算出三角形的斜邊高 * 記得要回傳double,畢竟可能是個小數 * 1pt/group ---- ### 範例輸入/輸出: ```shell= Input side1, side2 and hypotenuse height divide by blank: 3 4 5 The hypotenuse height is: 2.4 ``` P.S 用其他的直角三角形組合(例如:5, 12, 13;7, 24, 25) ---- ### 提示 ```cpp= #include<iostream> using namespace std; double calculate_hypotenuse_height( /* 這邊是不是需要指定一些參數? */ ){ // hypotenuse 是斜邊的意思 // 在這裡面計算上面的參數,並回傳結果(斜邊高) } int main(){ int s1, s2, hyp; cout<<"Input side1, side2 and hypotenuse height divide by blank: "; cin>>s1>>s2>>hyp; cout<<"The hypotenuse height is: "<< /* 這裡要呼叫那個函式才能得到答案 */ <<endl; return 0; } ``` ---- ### answer [GitHub 連結](https://github.com/tseng91301/bime_camp_tutorial/blob/main/practice/function_practice1.cpp) ---- ### inline function * 寫法跟一般函式一樣,只是前面加一個`inline` * 程式在編譯後會像是將內容直接寫在主程式裡面一樣 * 會增加執行速度(不須進行function pointer跳轉) * 但會讓執行檔變比較大 ```cpp= inline void hi(){ cout<<"Hello"<<endl; return; } ``` ---- ### 遞迴函數 * 在一個函數中執行一個相同的函數,以達到一些目的(一種策略) * 要注意函數是否有中止條件,否則容易出現無窮迴圈 ---- ### Example (以費式數列計算為例) ```cpp= #include <iostream> using namespace std; int fibonacci(int n) { if (n == 0) { return 0; } else if (n == 1) { return 1; } else { return fibonacci(n - 1) + fibonacci(n - 2); } } int main() { int number = 10; cout << "Fibonacci number at position " << number << " is " << fibonacci(number) << endl; // Output: Fibonacci number at position 10 is 55 return 0; } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/fibonacci.cpp) ---- ### Function Overload * 使用相同的函數名稱創建多個函數 * 各個函數之間一定要使用***不同的參數種類*** * 當一個函數要處理多種資料型態時可能會用到 ---- ### Example ```cpp= #include<iostream> using namespace std; int add_num(int i1, int i2){ int result = i1 + i2; return result; } double add_num(double i1, double i2){ double result = i1 + i2; return result; } int main(){ cout<<add_num(12, 13)<<endl; // 25 cout<<add_num(0.2, 0.4)<<endl; // 0.6 } ``` --- ## C++ class ---- ### class 架構 1. class 宣告 (class 名稱) 2. private 區塊 (只有在class內的function才能存取到的部分) 3. protected 區塊 (與private相似,只是繼承的class也能使用) 4. public 區塊 (外部也可以存取到的部分) ---- ### A brief example of class ```cpp= #include <iostream> #include <stdexcept> using namespace std; /*寫完可以是看看自己寫一個==的邏輯運算子(Fraction無法直接使用==)*/ class Fraction { private: int numerator; // 分子 int denominator; // 分母 public: void reduce() { // 用來簡化分數的函式(約分) int gcdValue = gcd(numerator, denominator); // 取分子和分母的最大公因數 numerator /= gcdValue; denominator /= gcdValue; } int gcd(int a, int b) { // 用來算出最大公因數的function while (b != 0) { int t = b; b = a % b; a = t; } return a; } int lcm(int a, int b){ // 用來計算最小公倍數的function return (a / gcd(a, b)) * b; } // Implementation function Fraction(int num, int denom) : numerator(num), denominator(denom) { if (denominator == 0) { // 如果分母 = 0 時 throw invalid_argument("Denominator cannot be zero"); // throw 出一個 Denominator cannot be zero 的錯誤訊息 } reduce(); // 對輸入的分數約分 } . . . 太多了... . . . Fraction result = f1.add(f2); cout << "Sum: "; result.print(); result = f1 + f2; cout << "Sum (function): "; result.print(); result = f1 - f2; cout << "Difference: "; result.print(); result = f1.subtract(f2); cout << "Difference (function): "; result.print(); result = f1 * f2; cout << "Product: "; result.print(); result = f1.multiply(f2); cout << "Product (function): "; result.print(); result = f1 / f2; cout << "Quotient: "; result.print(); result = f1.divide(f2); cout << "Quotient (function): "; result.print(); } catch (const exception& e) { cerr << e.what() << endl; // e.what() 輸出上面throw的錯誤訊息, cerr輸出錯誤訊息 } return 0; } ``` ##### [GitHub 連結在此](https://youtu.be/dQw4w9WgXcQ?si=kE_3cN1ZEJJn9ZyH) ---- ###### [<span style="color: white;">好啦正常的在這裡</span>](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/class_example_new.cpp) ###### [https://github.com/tseng91301/bime_camp_tutorial/blob/main/class_example.cpp](https://youtu.be/dQw4w9WgXcQ?si=kE_3cN1ZEJJn9ZyH) ###### [<span style="color: white;">再騙人我是狗</span>](https://www.youtube.com/watch?v=cfe8TiNp1EY&ab_channel=PeterChi) ---- ### 該來學MarkDown了吧 ``` ###### [<span style="color: white;">好啦正常的在這裡</span>](https://github.com/tseng91301/bime_camp_tutorial/blob/main/class_example_new.cpp) ###### [https://github.com/tseng91301/bime_camp_tutorial/blob/main/class_example.cpp](https://youtu.be/dQw4w9WgXcQ?si=kE_3cN1ZEJJn9ZyH) ###### [<span style="color: white;">再騙人我是狗</span>](https://www.youtube.com/watch?v=cfe8TiNp1EY&ab_channel=PeterChi) ``` ---- ### Implementation function(構件函數) ```cpp= Fraction(int num, int denom) { numerator = num; denominator = denom; if (denominator == 0) { // 如果分母 = 0 時 throw invalid_argument("Denominator cannot be zero"); // throw 出一個 Denominator cannot be zero 的錯誤訊息 } reduce(); // 對輸入的分數約分 } ``` * 通常寫在public區塊 * 是一個class被宣告時會跑的function(初始化) * 可以在裡面寫多個構件函數(function overload) ---- ### class的內部函數 * 可以寫在class的大括號內(就跟一般的function相同) * 若要寫在外面要加上`<class name>::` ```cpp= Fraction add(const Fraction& other){ // 與另一個分數相加 int commonDenominator = lcm(denominator, other.denominator); // 求通分的分母(用最小公倍數) int num1 = numerator * (commonDenominator / denominator); int num2 = other.numerator * (commonDenominator / other.denominator); int newNumerator = num1 + num2; return Fraction(newNumerator, commonDenominator); // 回傳相加之後的分數 } ``` ---- ### 取用class的內部元素 * 從class外部只能用public內的變數或函數 * class內部能夠使用或更改private, protected內的變數值 * 用法(外部): ```cpp= class_name.class_function(); ``` ---- ### class的運算子定義(overload) 使用`operator<運算子類型>`函數來實現 ```cpp= Fraction Fraction::operator+(const Fraction& f2){ // 設定 + 法運算子 return add(f2); } Fraction Fraction::operator-(const Fraction& f2){ // 設定 - 法運算子 return subtract(f2); } ``` ---- ### 各種operator... ![image](https://hackmd.io/_uploads/S1ChEhqUR.png) <font size=5>更多operator overload 請見 [learn.microsoft.com](https://learn.microsoft.com/zh-tw/cpp/cpp/operator-overloading?view=msvc-170)</font> ---- ### 對了!各位剛剛有沒有看到這個函數 ```cpp= int gcd(int a, int b) { // 用來算出最大公因數的function while (b != 0) { int t = b; b = a % b; a = t; } return a; } ``` ---- #### 其實這個就可以用**遞迴函數**完成,讓程式更好看喔 * 提示:使用輾轉相除法 * ![image](https://hackmd.io/_uploads/S1I-SksIA.png) * [YouTube 影片](https://www.youtube.com/watch?v=fGesPF3QA1U&ab_channel=Stepp%E5%AD%B8%E9%99%A2) ---- ### 答案在這邊 [GitHub 連結](https://github.com/tseng91301/bime_camp_tutorial/blob/main/practice/gcd_cal.cpp) --- ### 接著,讓我們來探討這一行 ```cpp= using namespace std; ``` ---- ### namespace 是啥?可以吃嗎 * `namespace`是一種命名空間,裡面包含了各式各樣的function * std是C++中的一個標準命名空間,裡面包含了string, vector等各種功能 ---- ### using namespace <命名空間名稱> * 讓相關的物件和函數預設使用此命名空間底下的 * 若沒有預設使用,則要在每個相關物件底下都加上`命名空間名稱::` ```cpp= #include<iostream> int main(){ std::string outp = "abc"; std::cout<<outp<<std::endl; } ``` ---- ### 如何自訂義namespace ```cpp= #include<iostream> namespace CustomNamespace { // 創建一個命名空間'CustomNamespace' void func(){ std::cout<<"This is from CustomNamespace"<<std::endl; } } void func(){ std::cout<<"This is from outside"<<std::endl; } int main(){ func(); CustomNamespace::func(); } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/custom_namespace.cpp) ---- ### std 常用函數, 物件 ---- ### std::cout 不用多說了吧 ---- ### std::cin * 與cout相反,是用來輸入文字的一種物件 * 用這種方法得到的文字沒辦法有空格 * 用`std::getline(std::cin, 要輸入的變數);`進行有空格的輸入 * Example: ```cpp= #include<iostream> int main(){ int inp; std::cout<<"Input a number: "; std::cin>>inp; std::cout<<"You type: "<<inp<<std::endl; } ``` ---- ### std::string * 是一個用來儲存字串的class * 使用時要引入cstring函式庫 * 包含很多字串處理的function(參考 https://cplusplus.com/reference/string/string/) * std另有`std::to_string(<int, double...>);`用來將數字轉換為string ---- ### string 用法: ```cpp= #include<iostream> #include<cstring> using namespace std; // 使用std命名空間 int main(){ string s1 = "abc"; // 指定s1是"abc" string s2; cout<<"Input s2: "; cin>>s2; // 讓用戶輸入s2 string s3 = s1 + s2; // 使用 '+' 將兩個string結合再一起,儲存結果於s3 cout<<s3<<endl; // 輸出s3字串 cout<<s3[3]<<endl; // 輸出s3的第3個字元 } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/string_example.cpp) ---- ### 小練習 - 凱薩加密法 * 指定一個字串"`S VYFO XDE LSWO CY WEMR, KC GOVV KC XDE LSWOMKWZ, IOOOO!`" * 輸入這個字串 * 對這個字串的每個字元進行減法運算(若超出A到Z範圍則+-26) * 會得到一個**有意義的句子** * 提示:會用到char運算、判斷式、string等 * 1pt/group ---- ### 答案在這邊 [GitHub 連結](https://github.com/tseng91301/bime_camp_tutorial/blob/main/practice/prac_caesar_enc.cpp) ---- ### std::vector * 一種容器,用來存放多個變數在一起並有序排列 * 是[動態陣列(array)](https://github.com/tseng91301/bime_camp_tutorial/blob/main/dynamic_pointer.cpp)的進化版 * 使用時要從後面加入資料,也是從後面刪除資料 * 初始化vector時,要指定存放的變數資料型態(`vector<資料型態> name`) * 更多資訊請見 https://cplusplus.com/reference/vector/vector/ ---- ### vector範例 ```cpp= #include<iostream> #include<vector> using namespace std; int main(){ vector<int> scores; // 宣告一個vector並指定存放的資料型態為int for(int a=0;a<100;a++){ scores.push_back(a+1); // 在後面插入a的值 } cout<<"Size of scores: "<<scores.size()<<endl; // scores.size() 輸出scores目前的長度 scores[49] = 1000; // 將scores的第49項改成1000 for(int a=99;a>=0;a--){ cout<<scores[a]<<", "; scores.pop_back(); // 刪除scores的最後一個元素 } cout<<endl; cout<<"Size of scores: "<<scores.size()<<endl; } ``` ##### [GitHub 連結在此](https://github.com/tseng91301/bime_camp_tutorial/blob/main/example/vector_example.cpp) ---- ### 小練習2 #### 還記得第(12, 6)頁的魔術方塊題目嗎?我們試試看用vector寫出來 * 善用vector的功能,我們這次讓使用者可以自訂義要幾x幾x幾的魔術方塊 * 其他功能均相同 * 不用害怕,真的很簡單! * 可以用上個魔術方塊程式來改 * 1pt/group ---- ### 範例輸出 ```shell= Please input the side length of the cube: 3 Please input the 1th plane of the cube: Row 1: Column 1: 1 Column 2: 2 Column 3: 3 Row 2: Column 1: 4 Column 2: 5 Column 3: 6 Row 3: Column 1: 1 Column 2: 2 Column 3: 3 Please input the 2th plane of the cube: Row 1: Column 1: 3 Column 2: 2 Column 3: 1 Row 2: Column 1: 6 Column 2: 5 Column 3: 4 Row 3: Column 1: 5 Column 2: 4 Column 3: 3 Please input the 3th plane of the cube: Row 1: Column 1: 1 Column 2: 4 Column 3: 3 Row 2: Column 1: 5 Column 2: 3 Column 3: 6 Row 3: Column 1: 4 Column 2: 2 Column 3: 5 Please input the 4th plane of the cube: Row 1: Column 1: 3 Column 2: 5 Column 3: 4 Row 2: Column 1: 2 Column 2: 3 Column 3: 6 Row 3: Column 1: 5 Column 2: 1 Column 3: 4 Please input the 5th plane of the cube: Row 1: Column 1: 1 Column 2: 1 Column 3: 3 Row 2: Column 1: 5 Column 2: 3 Column 3: 2 Row 3: Column 1: 4 Column 2: 6 Column 3: 3 Please input the 6th plane of the cube: Row 1: Column 1: 1 Column 2: 3 Column 3: 5 Row 2: Column 1: 4 Column 2: 6 Column 3: 2 Row 3: Column 1: 3 Column 2: 4 Column 3: 5 The 2nd plane of cube is: 3, 2, 1, 6, 5, 4, 5, 4, 3, ``` ---- ### 提示1 * vector<>,在<>裡面加的東西可以是任何東東,要是裡面再包一個vector\<int\>,是不是變成陣列裡面包陣列?(二維陣列的意思) * 要注意你在<>裡面給什麼,push_back()就要餵什麼! ---- ### 提示2 ```cpp= #include<iostream> #include<vector> using namespace std; int main(){ int sideLengthOfCube; cout<<"Please input the side length of the cube: "; cin>>sideLengthOfCube; // 藉由提示1,所以三維陣列怎麼宣告呢? for(int a=0;a<6;a++){ cout<<"Please input the "<<a + 1<<"th plane of the cube: "<<endl; // 這裡大家都用th,但其實1 是st,2 是nd,3 是rd,可以想看看怎麼改 vector<vector<int>> t1; // 第一層要初始化一個二維vector,放完資料再餵給三維vector吃 for(int b=0;b<sideLengthOfCube;b++){ cout<<"Row "<<(b+1)<<": "<<endl; // 以此類推,所以這邊要放什麼呢 - A? for(int c=0;c<sideLengthOfCube;c++){ cout<<"Column "<<(c+1)<<": "; int t3; cin>>t3; // t3 是輸入的數字,要如何放到 A 裡面? } t1.push_back(t2); // 將資料加入二維vector } cube.push_back(t1); //將二維陣列放到三維vector裡面 } cout<<"The 2nd plane of cube is: "<<endl; for(int a=0;a<sideLengthOfCube;a++){ for(int b=0;b<sideLengthOfCube;b++){ // 要叫出vector裡面的值其實跟陣列的方法一樣喔,所以要放什麼? } cout<<endl; } } ``` ---- ### 答案在這邊 [GitHub 連結](https://github.com/tseng91301/bime_camp_tutorial/blob/main/practice/vector_practice1.cpp) --- ### 再看一次這個程式就了解這是什麼了吧 ```cpp= class Fraction { private: int numerator; // 分子 int denominator; // 分母 public: void reduce() { // 用來簡化分數的函式(約分) int gcdValue = gcd(numerator, denominator); // 取分子和分母的最大公因數 numerator /= gcdValue; denominator /= gcdValue; } int gcd(int a, int b) { // 用來算出最大公因數的function while (b != 0) { int t = b; b = a % b; a = t; } return a; } int lcm(int a, int b){ // 用來計算最小公倍數的function return (a / gcd(a, b)) * b; } // Implementation function Fraction(int num, int denom) { numerator = num; denominator = denom; if (denominator == 0) { // 如果分母 = 0 時 throw invalid_argument("Denominator cannot be zero"); // throw 出一個 Denominator cannot be zero 的錯誤訊息 } reduce(); // 對輸入的分數約分 } Fraction add(const Fraction& other){ // 與另一個分數相加 int commonDenominator = lcm(denominator, other.denominator); // 求通分的分母(用最小公倍數) int num1 = numerator * (commonDenominator / denominator); int num2 = other.numerator * (commonDenominator / other.denominator); int newNumerator = num1 + num2; return Fraction(newNumerator, commonDenominator); // 回傳相加之後的分數 } Fraction subtract(const Fraction& other){ // 與另一個分數相減 int commonDenominator = lcm(denominator, other.denominator); // 求通分的分母(用最小公倍數) int num1 = numerator * (commonDenominator / denominator); int num2 = other.numerator * (commonDenominator / other.denominator); int newNumerator = num1 - num2; return Fraction(newNumerator, commonDenominator); // 回傳相減之後的分數 } Fraction multiply(const Fraction& other){ // 與另一個分數相乘 int newNumerator = numerator * other.numerator; // 分子相乘 int newDenominator = denominator * other.denominator; // 分母相乘 return Fraction(newNumerator, newDenominator); // 回傳相乘之後的分數 } Fraction divide(const Fraction& other){ if (other.numerator == 0) { // 如果除數(分數)是0 throw invalid_argument("Cannot divide by zero fraction"); // throw 出一個 Denominator cannot be zero fraction 的錯誤訊息 } // 進行分數相除的計算 int newNumerator = numerator * other.denominator; int newDenominator = denominator * other.numerator; return Fraction(newNumerator, newDenominator); // 回傳相除後的分數 } Fraction operator+(const Fraction&); Fraction operator-(const Fraction&); Fraction operator*(const Fraction&); Fraction operator/(const Fraction&); void print(){ // 將分數顯示出來 cout << numerator << "/" << denominator << endl; } }; ``` --- #### 如果可以了 ![image](https://hackmd.io/_uploads/BJMossF8C.png) --- #### 那就... ![1719413441300](https://hackmd.io/_uploads/rytnjoKIA.jpg =x500) --- ### 進行實作的部分吧!! ![1719413441290](https://hackmd.io/_uploads/By6kooKUC.jpg =x400) ###### 3x 圖片來源: IG: @xup6kun ---- ### ZeroJudge - 陪伴我高中三年的程式解題系統 ![image](https://hackmd.io/_uploads/rkWfyxo8R.png) ---- ### [a004. 文文的求婚](https://zerojudge.tw/ShowProblem?problemid=a004) #### 提示:這一題(輸入直到EOF)的cin輸入要這樣寫: ```cpp= int year; while(cin>>year){ // Your code } ``` * 0.5pt/group ---- ### [a244. 新手訓練 ~ for + if](https://zerojudge.tw/ShowProblem?problemid=a244) #### 提示: * 兩個數字相乘有時候會超出int範圍,用`long long`計算吧 ```cpp= long long n1, n2; ``` * 這題用`switch`可以讓程式碼比較好看,但`if-else`也沒問題~ * 0.5pt/group ---- ### [a006. 一元二次方程式](https://zerojudge.tw/ShowProblem?problemid=a006) #### 題示:要使用C++的開根號、次方功能,要引入math.h函式庫 ```cpp= pow(2, 10); // 2^10 sqrt(2); // 2^(1/2) ``` * 0.5pt/group ---- ### 對了,在這邊容我偷偷自肥一下 * Instagram: [@t.c.k_319](https://www.instagram.com/t.c.k_319/) * GitHub: [tseng91301](https://github.com/tseng91301) <font size=3>咖哩不能拌,鑫鑫腸不能吃 -- 6小隊輔「阿瑋」 曰</font> * ?pt(s)/group ---- ### [b294. 經濟大恐荒](https://zerojudge.tw/ShowProblem?problemid=b294) 0.5pt/group ---- ### [a010. 因數分解](https://zerojudge.tw/ShowProblem?problemid=a010) 0.5pt/group ---- ### [a040. 阿姆斯壯數](https://zerojudge.tw/ShowProblem?problemid=a040) #### 提示:雖然C++內建有pow這個開次方的function,但他似乎[不太精準](https://learn.microsoft.com/zh-tw/cpp/c-runtime-library/reference/pow-powf-powl?view=msvc-170)? 1pt/group ---- ### [o077. 2. 電子畫布](https://zerojudge.tw/ShowProblem?problemid=o077) #### 提示: * 這一題寫完可以[去考APCS](https://apcs.csie.ntnu.edu.tw/) * 雖然是陣列題,但也可以用vector(推薦) 3pts/group ---- ### [a065. 提款卡密碼](https://zerojudge.tw/ShowProblem?problemid=a065) 0.5pt/group ---- ### [a121. 質數又來囉](https://zerojudge.tw/ShowProblem?problemid=a121) #### 提示:雖然題目上沒有說,但似乎要這樣寫 ```cpp= while(cin>>l>>h){ // Your code } ``` 1pt/group ---- ### [a224. 明明愛明明](https://zerojudge.tw/ShowProblem?problemid=a224) #### 提示:應該不會有人想要真的將字串排列組合吧 #### 有沒有什麼規律?? 1pt/group
{"title":"C++程式教學","description":"image","contributors":"[{\"id\":\"49b092e5-357f-4529-be92-3ab18a9c88fb\",\"add\":72396,\"del\":37450}]"}
    910 views