Try   HackMD

Image Not Showing Possible Reasons
  • The image was uploaded to a note which you don't have access to
  • The note which the image was originally uploaded to has been deleted
Learn More →

什麼是C/C++?

  • 跨平台語言
  • Bjarne Stroustrup 開發
  • 系統資源和內存高度控制
  • 可移植性,應用在多個平台
  • C++是C語言的延伸
  • 函式導向 vs 物件導向

怎麼編譯運行?

可以參照這一篇
https://hackmd.io/@Chunghao/rJBkfDQ1T


C基礎語法

針對基礎輸出輸入

#include <stdio.h> int main() { const int name = 20; printf("%d", name); return 0; }
  1. #include <stdio.h>為C語言當中的標頭檔,表示輸出與輸入
  2. printf or scanf 為C語言的輸出方法
  3. 需要有修飾子進行資料型態判別

修飾子

- + %c
向左靠齊 印出正負號 字元
%s %d %f
字串 十進位整數 浮點數
%l %u %e
長整數,加在d,u之前 無號 十進位整數 浮點數(指數)

C語言必須要使用,C++則沒有必要使用

C++基礎語法

針對基礎輸出輸入

#include <iostream> using namespace std; int main() { cout << "Hello World\n"; return 0; }
  1. 這段程式碼是基礎語法中最常見的。
  2. #include <iostream> 是指引入標準庫中的輸入跟輸出,為標頭檔。
  3. using namespace std; std指得是逼準時是庫的命名空間。
  4. cout 為輸出指令。
  5. return 0 為回傳0給作業系統

std:: 與 namespace std; 差別

#include <iostream> int main() { std::cout <<"Hello World" ; return 0; }
  1. using namespace std;拿掉
  2. std:: 語法上可以取代

\n 與 endl 差異

#include <iostream> int main() { std::cout << "Hello World " << std::endl; return 0; }

變量

#include <iostream> #include <string> using namespace std; int main() { int name = 51; // 整數型態 = 2位元 = 可以在int前面 + unsigned or short or long float name2 = 5.2; // 浮點數 = 4位元 = 用於小數點 and 指數型態 double mane22 = 5.2; //被精度浮點數 = 8位元 = 用於小數點 and 指數型態 char name3 = 'F'; // 字元 = 1位元 = 儲存英文字母 and ASCII碼 string name4 = "project"; // 字串 = 4位元 = 表示一串話 bool name5 = true; //布林 = 1位元 = 用於判斷true or false cout << name << endl << name2 << endl << mane22 << endl << name3 << endl << name4 << endl << name5 << endl; return 0; }

此時輸出分別為51, 5.2, 5.2, F, project, 1

命名原則

  • 不能使用關鍵字
  • 前8為有效字元
  • 可以使用英文字母,數字,底線
  • 名稱中間不能有空白
  • 第一個字元不可為數字
  • 名稱要有意義,長短適中
  • 大小寫有別

常量

#include <iostream> using namespace std; int main() { const int name = 51; cout << name ; return 0; }

如果在int 前面新增一個const 代表這個值無法變動,如果再賦值給name的話就會報錯。


跳脫字元

\n
換行
\f \t \b
換頁 跳格 倒退
" ' /
雙引號 單引號 斜線
\ \x \d
反斜線 ASCII(16進位) ASCII(8進位)

運算符

算術運算子

+ - * / %(取餘數)
a+b a-b a*b a/b a%b

關係運算子

> >= < <= == != += -= *= /= %=
2>3 =false 2>=3 =false 2<3 =true 2<=3 =true 2==3 =false 2!=3 =true a=a+b -> a+=b a=a-b -> a-=b a=ab -> a=b a=a/b -> a/=b a=a%b -> a%=b
#include <iostream> using namespace std; int main() { int x=1,y=2; cout << x+y << endl; cout << x-y << endl; cout << x*y << endl; cout << x/y << endl; x =1; //用於計算初始化,否則答案會報錯 cout << (x+=y) << endl; //3 x =1; cout << (x-=y) << endl; //-1 x =1; cout << (x*=y) << endl; //2 x =1; cout << (x/=y) << endl; //0 x =1; cout << (x%=y) << endl; //1 return 0; }

C++運算輸出練習


#include <stdio.h> int main() { int x=1,y=2; printf("%d\n", x+y); printf("%d\n", x-y); printf("%d\n", x*y); printf("%d\n", x/y); x =1; //用於計算初始化,否則答案會報錯 printf("%d\n", (x+=y)); //3 x =1; printf("%d\n", (x-=y)); //-1 x =1; printf("%d\n", (x*=y)); //2 x =1; printf("%d\n", (x/=y)); //0 x =1; printf("%d\n", (x%=y)); //1 return 0; }

C運算輸出練習

可以看得出來C/C++語法上有些許上的差異。

邏輯運算子

&& || !
AND or 且 OR or 或 NOT or 否

遞增/減運算子

i++ i- - ++i - -i
表示先輸出i,再對i做出加法 表示先輸出i,再對i做出減法 先對i進行加法,再輸出i 先對i進行減法,再輸出i

資料型態轉換

#include <iostream> #include <typeinfo> //轉換類型查看類型的標頭檔 using namespace std; int main() { int x = 5; double y = x; // 隱式轉換 double pi = 3.14159265359; int approxPi = static_cast<int>(pi); // 顯式轉換 double pi2 = 3.14159265359; int approxPi2 = (int)pi2; // C風格類型轉換 cout << typeid(approxPi2).name() << endl; return 0; }

C++資料型態的轉換

#include <stdio.h> int main() { int x = 5; double y = 3.0; double result = x + y; //算數轉換 int x = 5; double y = (double)x; //指派轉換 double pi = 3.14159265359; int approxPi = (int)pi; //強制轉換,也叫做模式轉換 printf("%f\n", pi); return 0; }

C資料型態的轉換
C語言沒有查閱型別的函式庫

數學運算

  1. C語言中使用:#include <math.h>
  2. C++語言中使用:#include <cmath>
#include <iostream> #include <cmath> //C++數學函式庫 using namespace std; int main() { cout << max(5, 10) << endl; // 返回較大的數(最大值函數) cout << min(5, 10) << endl; // 返回較小的數(最小值函數) cout << sqrt(64) << endl; // 返回一個數的平方根 cout << round(2.6) << endl; // 四捨五入到最近的整數 cout << log(2) << endl; // 自然對數(以e為底的對數) cout << abs(5) << endl; // 返回一個整數的絕對值 cout << acos(0.5) << endl; // 返回一個數的反余弦(弧度) cout << asin(0.5) << endl; // 返回一個數的反正弦(弧度) cout << atan(5) << endl; // 返回一個數的反正切(弧度) cout << atan2(5, 3) << endl; // 返回兩個數的反正切(弧度) cout << ceil(5.6) << endl; // 向上取整數 cout << cos(5) << endl; // 返回一個數的余弦(弧度) cout << cosh(5) << endl; // 返回一個數的雙曲余弦 cout << exp(5) << endl; // 返回e的指數幂 cout << expm1(5) << endl; // 返回e的指數幂減去1 cout << fabs(-5) << endl; // 返回一個浮點數的绝對值 cout << fdim(5, 3) << endl; // 返回兩個數中較大的差值 cout << floor(5.6) << endl; // 向下取整數 cout << hypot(5, 3) << endl; // 返回兩個數的平方和的平方根 cout << fma(5, 3, 2) << endl; // 返回兩個數的乘積加上第三個數 cout << fmax(5, 3) << endl; // 返回兩個數中較大的數 cout << fmin(5, 3) << endl; // 返回兩個數中较小的數 cout << fmod(5, 3) << endl; // 返回兩個數相除的余數 cout << pow(5, 3) << endl; // 返回一個數的幂 cout << sin(5) << endl; // 返回一個數的正弦(弧度) cout << sinh(5) << endl; // 返回一個數的雙曲正弦 cout << tan(5) << endl; // 返回一個數的正切(弧度) cout << tanh(5) << endl; // 返回一個數的雙曲正切 return 0; }

結果回傳:

max(5, 10)=10 min(5, 10)=5 sqrt(64)=8 round(2.6)=3 log(2)=0.693147 abs(5)=5 acos(0.5)=1.0472 asin(0.5)=0.523599 atan(5)=1.3734 atan2(5, 3)=1.03038 ceil(5.6)=6 cos(5)=0.283662 cosh(5)=74.2099 exp(5)=148.413 expm1(5)=147.413 fabs(-5)=5 fdim(5, 3)=2 floor(5.6)=5 hypot(5, 3)=5.83095 fma(5, 3, 2)=17 fmax(5, 3)=5 fmin(5, 3)=3 fmod(5, 3)=2 pow(5, 3)=125 sin(5)=-0.958924 sinh(5)=74.2032 tan(5)=-3.38052 tanh(5)=0.999909

C++撰寫數學函式庫


#include <stdio.h> #include <math.h> // C數學庫 #include <stdlib.h> //管理內存、随機數生成、字符串轉換、進程控制 int main() { int result; int x = 5, y = 10; result = (x > y) ? x : y; // 返回為較大的數 printf("max(5, 10) = %d\n", result); result = (x < y) ? x : y; // 返回較小的數 printf("min(5, 10) = %d\n", result); double sqrtValue = sqrt(64); // 返回一個數的平方根 printf("sqrt(64) = %lf\n", sqrtValue); double roundValue = round(2.6); // 四捨五入到最近的的整數 printf("round(2.6) = %lf\n", roundValue); double logValue = log(2); // 自然對數(以e為底的對數) printf("log(2) = %lf\n", logValue); int absValue = abs(5); // 返回一個整數的绝對值 printf("abs(5) = %d\n", absValue); double acosValue = acos(0.5); // 返回一個數的反余弦(弧度) printf("acos(0.5) = %lf\n", acosValue); double asinValue = asin(0.5); // 返回一個數的反正弦(弧度) printf("asin(0.5) = %lf\n", asinValue); double atanValue = atan(5); // 返回一個數的反正切(弧度) printf("atan(5) = %lf\n", atanValue); double atan2Value = atan2(5, 3); // 返回兩個數的反正切(弧度) printf("atan2(5, 3) = %lf\n", atan2Value); double ceilValue = ceil(5.6); // 向上取整數 printf("ceil(5.6) = %lf\n", ceilValue); double cosValue = cos(5); // 返回一個數的余弦(弧度) printf("cos(5) = %lf\n", cosValue); double coshValue = cosh(5); // 返回一個數的雙曲余弦 printf("cosh(5) = %lf\n", coshValue); double expValue = exp(5); // 返回e的指數幂 printf("exp(5) = %lf\n", expValue); double expm1Value = expm1(5); // 返回e的指數幂減去1 printf("expm1(5) = %lf\n", expm1Value); double fabsValue = abs(-5); // 返回一個浮點數的绝對值 printf("abs(-5) = %lf\n", fabsValue); double fdimValue = fdim(5, 3); // 返回兩個數中較大的差值 printf("fdim(5, 3) = %lf\n", fdimValue); double floorValue = floor(5.6); // 向下取整數 printf("floor(5.6) = %lf\n", floorValue); double hypotValue = hypot(5, 3); // 返回兩個數的平方和的平方根 printf("hypot(5, 3) = %lf\n", hypotValue); double fmaValue = fma(5, 3, 2); // 返回兩個數的乘積加上第三個數 printf("fma(5, 3, 2) = %lf\n", fmaValue); double fmaxValue = fmax(5, 3); // 返回兩個數中較大的數 printf("fmax(5, 3) = %lf\n", fmaxValue); double fminValue = fmin(5, 3); // 返回兩個數中較小的數 printf("fmin(5, 3) = %lf\n", fminValue); double fmodValue = fmod(5, 3); // 返回兩個數相除的餘數 printf("fmod(5, 3) = %lf\n", fmodValue); double powValue = pow(5, 3); // 返回一個數的幂 printf("pow(5, 3) = %lf\n", powValue); double sinValue = sin(5); // 返回一個數的正弦(弧度) printf("sin(5) = %lf\n", sinValue); double sinhValue = sinh(5); // 返回一個數的雙曲正弦 printf("sinh(5) = %lf\n", sinhValue); double tanValue = tan(5); // 返回一個數的正切(弧度) printf("tan(5) = %lf\n", tanValue); double tanhValue = tanh(5); // 返回一個數的雙曲正切 printf("tanh(5) = %lf\n", tanhValue); return 0; }

結果回傳:

max(5, 10) = 10 min(5, 10) = 5 sqrt(64) = 8.000000 round(2.6) = 3.000000 log(2) = 0.693147 abs(5) = 5 acos(0.5) = 1.047198 asin(0.5) = 0.523599 atan(5) = 1.373401 atan2(5, 3) = 1.030377 ceil(5.6) = 6.000000 cos(5) = 0.283662 cosh(5) = 74.209949 exp(5) = 148.413159 expm1(5) = 147.413159 abs(-5) = 5.000000 fdim(5, 3) = 2.000000 floor(5.6) = 5.000000 hypot(5, 3) = 5.830952 fma(5, 3, 2) = 17.000000 fmax(5, 3) = 5.000000 fmin(5, 3) = 3.000000 fmod(5, 3) = 2.000000 pow(5, 3) = 125.000000 sin(5) = -0.958924 sinh(5) = 74.203211 tan(5) = -3.380515 tanh(5) = 0.999909

C撰寫數學函式庫

C語言沒有max,min的函數,因此要使用這種比大小方法回傳最大值。

result = (x > y) ? x : y; // 返回為較大的數 printf("max(5, 10) = %d\n", result);

#define是什麼?

  • C語言能用的,C++都可以做使用。
  • 宏定義命令,由以下規範所組成
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
  • 簡單的宏定義如下:
#define <宏名> <字符串>
  • 帶有參數的宏定義:
#define <宏名> (<參數表>) <宏體>
  1. 簡單的宏定義:
#include <iostream> using namespace std; #define PI 3.14 int main(){ double radius; cout << "請輸入半徑:"; cin >> radius; double area = PI * radius * radius; double circumference = 2 * PI * radius; cout << "圖的面積是:" << area << endl; cout << "圖的周長是:" << circumference << endl; return 0; }
  1. 這個意思是指定義一個簡單的宏定義PI的值為3.14,因此在main當中調用PI就可以代表3.14這個數值。
  2. Ans:
    請輸入半徑:2
    圖的面積是:12.56
    圖的周長是:12.56
  1. 帶有參數的宏定義:
#include <iostream> using namespace std; #define SQUARE(x) ((x) * (x)) int main() { int num = 5; int squared = SQUARE(num); cout << "數字 " << num << " 的平方是 " << squared << endl; return 0; }
  1. 這個意思是指定義一個帶有參數的宏定義SQUARE,其參數x的方法為x*x,在main當中宣告num = 5,帶入SQUARE的方法,再將結果賦值給squared。
  2. Ans:數字5的平方是25

條件語句

1. if/else

  • C語言:
#include <stdio.h> int main() { int number; printf("enter an integer:"); scanf("%d",&number); if (number > 0) { printf("The number is positive.\n"); } else if (number < 0) { printf("The number is negative.\n"); } else { printf("The number is zero.\n"); } return 0; }
  • 這邊使用到條件式,&number這個用法代表把值帶入number。
#include <stdio.h> int main() { for (int i = 0; i < 5; i++) { for (int j = i + 1; j > 0; j--) { printf("*"); } printf("\n"); } return 0; }
*
**
***
****
*****

i=層數, j=*數,C++基本上只有輸出入的部分改掉。

  • C++語言:
#include <iostream> int main() { int number; std::cout << "Enter an integer: "; std::cin >> number; if (number > 0) { std::cout << "The number is positive." << std::endl; } else if (number < 0) { std::cout << "The number is negative." << std::endl; } else { std::cout << "The number is zero." << std::endl; } return 0; }
  • 這邊使用 " cin >> number" 把值帶入number,基本上與C大同小異。
  • 值得一提的是C++語法當中有一個有趣的寫法(Cpp專屬條件表達式,雜項運算符),如下:
#include <iostream> using namespace std; int main() { int number = 20; string result = (number < 18) ? "goodmorning":"hi"; cout << result << endl; return 0; }
  • Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    Condition ? X : Y
string result = (number < 18) ? "goodmorning":"hi";

2. switch case

  • C語言:
#include <stdio.h> int main() { int choice; printf("選擇(1, 2, 3, 4):"); scanf("%d", &choice); switch (choice) { case 1: printf("你選了 1\n"); break; case 2: printf("你選了 2\n"); break; case 3: printf("你選了 3\n"); break; case 4: printf("你選了 4\n"); break; default: printf("無效\n"); } return 0; }
  • C++語言:
#include <iostream> using namespace std; int main() { int choice; cout << "選擇(1, 2, 3, 4):"; cin >> choice; switch (choice) { case 1: cout << "你選擇了 1" << endl; break; case 2: cout << "你選擇了 2" << endl; break; case 3: cout << "你選擇了 3" << endl; break; case 4: cout << "你選擇了 4" << endl; break; default: cout << "無效" << endl; } return 0; }

3. While-do

  • C語言:
#include <stdio.h> int main() { int number = 2; while (1) { if (number > 0) { printf("The number is positive.\n"); } else if (number < 0) { printf("The number is negative.\n"); } else { printf("The number is zero.\n"); } break; } return 0; }
  • C++語言:
#include <iostream> using namespace std; int main() { int number = 2; while (true) { if (number > 0) { cout << "The number is positive." << endl; } else if (number < 0) { cout << "The number is negative." << endl; } else { cout << "The number is zero." << endl; } break; } return 0; }

while(ture) or while(1),都代表為真的意思,都會執行無限迴圈。


陣列

  • C語言:
#include <stdio.h> #include <string.h> int main() { char timer[][15] = {"jack", "chris", "anderson", "kevin"}; printf("%s\n", timer[2]); return 0; }
  • 這邊代表定義一個timer二維陣列,每個陣列的數組限制大小為15,從0開始數到2,其輸出為anderson
  • 值得注意的是,C語言當中,並沒有string的用法,字串使用char
  • C++語言:
#include <iostream> using namespace std; int main() { string timer [] ={"jack","chris","anderson","kevin"}; cout << timer[2] <<endl; return 0; }

Function

  1. 介紹
  2. 宣告
  3. 函式中的參數
  4. 變數的範圍
  5. 靜態變數

介紹

一段程式碼,執行特別的工作。
可重複使用
讓程式變得淺顯易懂
在呼叫函式不變的狀況下,可以直接修改程式。

宣告

回傳值型態 函式名稱 (參數)
  • 沒有回傳值:void square(int length)
#include <stdio.h> void square(int length); //宣告function 沒有回傳值 void square(int length){ //撰寫function內容 } int main(){ square(); //傳入square function的參數 }
  • 有回傳值:int square(int len)
#include <stdio.h> int square(int length); //宣告function int square(int length){ int x =0; x = length * length; //function內執行的程式碼 return x; } int main(){ int len = 5; int ans; ans = square(len); //主function中len的值帶入square function printf("square = %d\n",ans); return 0; }
  • Function中的參數
#include <stdio.h> // 函式原型(宣告),宣告時只需要指定型態即可 int sum(int, int); int main() { int num1 = 5, num2 = 6; // 設定兩個數字 int ans; // 答案存入ans中 ans = sum(num1, num2); // 傳入sum函式的參數有兩個(5, 6) printf("sum = %d\n", ans); return 0; } // 定義函式,有兩個 int sum(int a, int b) { int x = 0; // 將答案存入x中 x = a + b; return x; // 將答案傳回主程式 }
  • 變數的範圍
  1. global (全域變數)整個程式中皆可以使用此變數
  2. local (區域變數)只能在部分函式中使用
    範例如下:
#include <stdio.h> int global1 = 0; // 全域變數 int main() { int local1 = 2, local2 = 3; // 區域變數 global1 = local1 + local2; printf("%d \n", global1); /* 5 */ return 0; }
  • 靜態變數(Static Variable)
  1. static變數,是一個區域變數,但不會因為函式執行結束,變數內的資料就不見。
#include <stdio.h> void hello(); void hello(){ static int x = 0; printf("hello world: %d\n",x); x++; } int main(){ for(int i = 0; i < 5; i++){ hello(); } return 0; }

上面的for迴圈,是為了要進入此函式五次才設定的。函式內部的X沒有因為離開函式後,其數字重新歸零,而是繼續累加。這就是static變數的最大特色。要等到程式全部結束後,X才會回到原本預設的狀態。


指標 pointer

指標是用來儲存"記憶體位址"的變數,設計師可以透過指標來管理記憶體,包含:配置,存值,取值,釋放

  1. & : 取得已存在的變數位址
  2. * : 表示為指標變數
  3. new : 配置指定型別的記憶體位址
  4. 另一個指標:將已經存在的指標所儲存的記憶體位址指派給另一個指標

宣告

type* name; // *就代表指標 這個指標為name type* name = address; type* nameA,* nameB;
  • 基本存取功能
#include <iostream> using namespace std; int main(){ int a ; a = 8; cout << a << endl; int * p; // 建立一個指標 p的變數,裡面存放一個記憶體位址 p = &a; // 這邊指向記憶體a cout << p << endl; //因此得出p裡面裝得是a的記憶體位址 *p = 10; // 這邊是用來管理記憶體位址裡的值, 這邊將p記憶體位址裡的值變成10 cout << *p << endl; // 因此裡面的值會改成10 cout << a << endl; // 由於p是指向a的記憶體位址,因此裡面的值也會改成10 return 0; }
  • new 的用法
int * r; r = new int; //另外派一個空間給r *r =100; //管理空間裡的值 cout << r << endl; //得到r的位址 cout << *r << endl; //得到該位址中的值 return 0;
  • 另一個指標 的用法
int * r; r = new int; //另外派一個空間給r * r =100; //管理空間裡的值 cout << r << endl; //得到r的位址 cout << *r << endl; //得到該位址中的值 int * s = r; //建立一個指標s 存放r的記憶體位址 * s =200; //管理該記憶體中的值改為200 cout << * r << endl; //輸出後值為200 return 0;

delete 用法

  1. 釋放記憶體位址
  2. 用 new 配置給指標的記憶體可以用delete釋放
int * r; r = new int; *r =100; cout << r << endl; //得到r的位址 cout << *r << endl; //得到該位址中的值 int * s = r; // 建立一個指標s 存放r的記憶體位址 * s =200; //管理該記憶體中的值改為200 cout << * r << endl; //輸出後值為200 delete r; // 把該位址的空間砍掉 cout << r << endl; // 0x12d606ac0 cout << * r << endl; // 0 cout << s << endl; //0x12d606ac0 cout << * s << endl; // 0 return 0;

const 用法

  1. 限制指標or指標所管理的記憶體為 read-only
  2. type * const name;
  3. const type * name;
int * const m = new int ; *m =100; *m =200; cout << *m <<endl; //200
  • 如果這邊再寫delete釋放掉m的位址

type * const name;

int * const m = new int ; //配了一個新的位址給m *m =100; //指標管理裡面的值 *m =200; //可以二次更改裡面的值 cout << *m <<endl; delete m; m = new int ; return 0;
 error: cannot assign to variable 'm' with const-qualified type 'int *const'

因此可得出const 只可讀取

const type * name;

int * const m = new int ; //配了一個新的位址給m *m =100; //指標管理裡面的值 *m =200; //可以二次更改裡面的值 cout << *m <<endl; const int * n = new int ; //給予新的位址 *n=100; //將*n的值給為100 cout << *n <<endl; return 0;
error: read-only variable is not assignable

這樣寫n還沒定義值就先報錯無改給值,你可以得很奇怪,因此這邊不能這樣賦予他一個新的記憶體空間。

int * const m = new int ; //配了一個新的位址給m *m =100; //指標管理裡面的值 *m =200; //可以二次更改裡面的值 cout << *m <<endl; const int * n = m ; //將位址指向m cout << *n <<endl; //因此可以得出與m相同的值 return 0;

指標參數

  1. 以指標為參數 pass by pointer
  2. 呼叫function
  3. pass by value
  4. pass by point
  • main3.h
#include <iostream> using namespace std; void showValue(int * p); void showValue(int * p){ cout << *p << endl; }
  • main2.cpp
# include <iostream> using namespace std; #include "main3.h" int main(){ int a = 10; showValue(&a); //10 int * b = new int ; *b = 20; showValue(b); //20 return 0; }
  • 解釋


為何Value,&Value輸出結果不一樣?

  • main3.h
#include <iostream> using namespace std; void passbyValue(int Value); void passbyPoint(int * pValue); void passbyValue(int Value){ Value += 100; } void passbyPoint(int * pValue){ *pValue += 100; }
  • main2.cpp
# include <iostream> using namespace std; #include "main3.h" int main(){ int Value =1; passbyValue(Value); cout << Value << endl; //1 passbyPoint(&Value); cout << Value << endl; //101 return 0; }

解釋第一部分為何輸出1

解釋第二部分為何輸出=101


return 型別為指標

呼叫敘述是一個記憶體位址
不應return 區域變數的記憶體位址
可以宣告為const

  • main3.h
#include <iostream> using namespace std; int * getAddressA(){ int * p = new int ; // 新增一個記憶體位址 * p =100; // 記憶體位址中存在的值 return p; // return一個記憶體位址 } int * getAddressB(){ return new int ; //新增一個記憶體位址 } int * getAddressC( const int * p){ //以p為主本 int * q = new int ; //建立一個記憶體位址q *q = *p; // 賦值 *q += 100; //修改q的值 return q; //返回q }
  • main2.cpp
# include <iostream> using namespace std; #include "main3.h" int main(){ int * i = getAddressA(); // getAddressA 是一個記憶體位址 cout << * i << endl; // 這邊代表取值, int * j = getAddressB(); //代表一個記憶體位址 * j = 200; //設定值為200 cout << *j << endl; //輸出即刻為200 int *m = new int; // 新增一個記憶體位址 * m = 1; // 記憶體位址內值為1 int * n = getAddressC(m); // 新增一個記憶體位址n,調用getAddressC函式,參數m cout << *n << endl; return 0; }

指標的指標

  1. 宣告 type** name;
  2. 存取值 **name;
#include <iostream> using namespace std; int main(){ int ** a; a = new int *; //指標的記憶體位址 * a = new int; // 儲存可以放int型別的值的記憶體位址 ** a =10; // 存放值 cout << a << endl; // 記憶體位址 cout << * a << endl; // 記憶體位址 cout << ** a << endl; // 10 int * b = new int; *b = 20; a = &b; cout << ** a << endl; // 20 }

void *

  1. 指派
  2. casting
int * b = new int; *b = 20; a = &b; cout << ** a << endl; // 20 void * v; v = b; cout << b << endl; cout << v << endl; cout << *b << endl; // 錯誤訊息 cout << *v << endl; // 錯誤訊息 因為void 是通用型型別 無法確定型別是什麼,因此無法輸出 return 0;
  • 解決辦法
int * b = new int; *b = 20; a = &b; cout << ** a << endl; // 20 void * v; v = b; cout << b << endl; cout << v << endl; cout << *((int*)b) << endl; // casting cout << *((int*)v) << endl; // casting return 0;

指標陣列

  1. 宣告
  2. 存取
#include <iostream> using namespace std; int main(){ int * array[10]; // 新增一個指標陣列 for( int i = 0; i < 10; i++){ array[i] = new int; //初始化並新增一個陣列記憶體位址 *array[i] = i + 1; } for( int i = 0 ; i < 10 ; i++){ cout << *array[i] << endl; } return 0; }
  • [
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    ] 動態指標陣列

物件導向

物件

  • 何謂物件?

執行環境依照類別中的宣告,所配置的記憶體群組。它可以儲存一群資料,而該群資料可以完整的描述一個特定的運算單位。例如:一群可以完整描述一張訂單的資料、一群可以完整描述購買者的資料、一群可以完整描述一件商品的資料、一群可以完整描述日期時間的資料、一群可以完整描述信用卡付款的資料

也就是說:一個記憶體群組,代表一個特定運算單位的完整資料。這一個記憶體群組,我們就稱之為物件。

所以就程式語言的角度上也可以說:物件一群記憶體的集合。

物件擁有什麼
資料成員(Data Members) => 儲存資料(變數)。
很多文件的解說中稱之為屬性或狀態。在 Java 中稱之為欄位。

成員函式 (Member Functions) => 運算資料。
很多文件的解說中稱之為行為或能力。在 Java 中稱之為方法。

  • 為什麼要使用物件導向
    在物件導向之前是函式導向。隨著運算複雜度的提升,各語言也逐一的支援物件導向。物件導向與函式導向的基本差異是:物件儲存資料,物件運算資料。

  • 以 C / C++ 語言為例,在函式導向時期(C 語言時期),雖然可以用結構(struct)宣告一個新型別,集合一群資料。但是,那一群資料本身並不具有運算能力,它只能當參數讓函式運算。所以,資料與運算資料的函式是沒有關係的。

  • 到了物件導向時期(C++ 時期),改用類別(class)來宣告新型別。依照類別的宣告建立的物件,除了是一群資料的集合之外,本身也具有運算的能力。也就是說:資料與運算資料的函式是屬於同一個物件的成員。

  • 相同類別的物件,一定擁有相同的成員
    相同名稱的資料成員,但它的值不見得一樣。
    相同名稱的成員函式,但它運算的結果不一定一樣。
    每一個物件獨立管理與運算自己的資料
    除非特殊的設計需求。基本上,每個物件運算自己的資料。

函式導向 & 物件導向 差異

  • 函式導向
//函式導向,函式是老大 struct boot{ int redius; int height; double girth; double area; double volume; }; double getGirth(struct boot c); //結構指的是接受一群資料做運算
  • 物件導向
// 物件導向,物件是老大,物件擁有資料,且擁有運算能力 class Circle{ public: //存取限制一樣 int redius; int height; double girth; double area; double volume; double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } };
  • 執行物件導向
#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; cout << c1.getGirth() << endl; Circle c2; c2.redius = 10; cout << c2.getGirth() << endl; return 0; }
  • 總結
  1. 物件儲存資料,運送資料
  2. 物件導向不能用靜態觀點,不可看單一程式碼,要看真的主函式中真正配置的記憶體位址
  3. 環境依照類別做出來物件,類別是說明書,材料是記憶體,配出來的記憶體是物件

類別

  • 什麼是類別

像 int 一樣,是 C++ 中合法的型別。

  • 為什麼要開發類別

因為程式有物件的需求,所以依照物件的需求開發類別

  • 類別用來做什麼

宣告變數
宣告指標
宣告參考
物件轉型
物件識別
執行環境製做物件的說明書

  • 類別中有什麼

物件成員=> 物件資料成員,物件成員函式
類別成員=> 類別資料成員,類別成員函式
建構函式(建構子)
其他

/* 1.類別中全部叫做物件成員 2.class建立了一個Circle類別 3.宣告五個變數 = 物件資料成員 4.宣告一個函式 = 成員函式 */ class Circle{ //Circle 類別 public: // 存取限制 int redius; // 物件資料成員 int height; // 物件資料成員 double girth; // 物件資料成員 double area; // 物件資料成員 double volume; // 物件資料成員 // 成員函式 double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } };

物件變數

  • 宣告

ClassName objectName;

變數宣告時,環境會依照類別中的宣告來建立物件。並把物件的記憶體位址指派給變數。

class Circle{ public: //存取限制一樣 int redius; int height; double girth; double area; double volume; double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } };
#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; cout << c1.getGirth() << endl; Circle c2; c2.redius = 10; cout << c2.getGirth() << endl; return 0; }
  • 講解記憶體變化

標黃色代表他們是一體的,記憶體一定是一格一格的,不是一群物件,是一群記憶體,以群為單位配送記體位址複製給c1

到這裡,執行main函式,radius記憶體位址變數變成10,並且執行getGirth,再將結果複製給0x123(c1)

接著main函式當中,執行第二個c2,為何執行第二個c2,原本的記憶體位址不會消失呢?由下圖解釋:

  • 成員存取

objectName.dataMember
objectName.memberFunction()

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; // objectName.dataMember cout << c1.getGirth() << endl; // objectName.memberFunction() Circle c2; c2.redius = 10; cout << c2.getGirth() << endl; return 0; }

用 變數.成員 的方式,存取物件成員。

  • 指派

objectName = otherObjectName;

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; cout << c1.getGirth() << endl; // 62.8318 Circle c2; c2.redius = 20; cout << c2.getGirth() << endl; // 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 cout << "---------------" << endl; c1 = c2; cout << c1.getGirth() << endl; // 此時的c1輸出結果 = 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 return 0; }

變數和物件是挷定的,也就是說這個變數不能再指派另一個物件的記憶體位址給它。指派時是將 '=' 右邊的物件的資料成員的值,複製給 '=' 左邊的物件的資料成員。

  • 物件變數參數

函式原型 : void function(ClassName objectName)
呼叫敍述 : function(objectName);

在函式呼叫敍述的小括號中放物件變數時,是把本物件的成員變數的值,複製給函式的物件變數參數。

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; cout << c1.getGirth() << endl; // 62.8318 Circle c2; c2.redius = 20; cout << c2.getGirth() << endl; // 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 cout << "---------------" << endl; c1 = c2; cout << c1.getGirth() << endl; // 此時的c1輸出結果 = 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 cout << "---------------" << endl; int i = c1.compare(c2); // c2變成參數傳給compare if( i == 0){ cout << "valus are the same" << endl; }if else( i > 0){ cout << "c1 is bigger" << endl; }else{ cout <, "c2 is bigger" << endl; } return 0; }
// 物件導向,物件是老大,物件擁有資料,定且擁有運算能力 class Circle{ public: //存取限制一樣 int redius; int height; double girth; double area; double volume; double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } int compare(Circle c){ // void function(ClassName objectName) if( redius > c.redius){ return 1; }else if( redius < c.redius){ return -1; }else{ return 0; } } };
  • 用圖解釋記憶體變化
  1. int compare(Circle c) :
  2. c1.compare(c2) : 在c1中執行compare,所以將其記憶體位建立在c1中,並將物件的值指派一份給c
  3. c2物件的值指派給c的值
  4. 接著改變c2的值就會改變c.radius的值
  5. c2的值不會被改變,他是複製一份給c,並指派參數給c.radius,如果改變c.radius的值,c2依然為20

  • 物件變數返回值

函式原型 : ClassName function()
呼叫敍述 : ClassName objectName = function();

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.redius = 10; cout << c1.getGirth() << endl; // 62.8318 Circle c2; c2.redius = 20; cout << c2.getGirth() << endl; // 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 cout << "---------------" << endl; c1 = c2; cout << c1.getGirth() << endl; // 此時的c1輸出結果 = 125.664 cout << "c1 = " << &c1 << endl; // c1 = 0x16d63b340 cout << "c2 = " << &c2 << endl; // c2 = 0x16d63b320 cout << "---------------" << endl; int i = c1.compare(c2); if( i == 0){ cout << "valus are the same" << endl; }else if( i > 0){ cout << "c1 is bigger" << endl; }else{ cout << "c2 is bigger" << endl; } cout << c2.getGirth() << endl; cout << "---------------" << endl; Circle c3 = c2.copy(); //用類別作為返回型別 cout << c3.getGirth() << endl; // 125.664 return 0; }
// 物件導向,物件是老大,物件擁有資料,定且擁有運算能力 class Circle{ public: //存取限制一樣 int redius; int height; double girth; double area; double volume; double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } int compare(Circle c){ if( redius > c.redius){ return 1; }else if( redius < c.redius){ return -1; }else{ return 0; } } Circle copy(){ Circle c; c.redius = redius; return c; } };

代表返回值是這個類別的物件變數
不過這樣有個壞處,這個寫法會多佔用一個記憶體位址,如果是大型運算會佔太多不必要的空間

物件指標

  • 宣告

ClassName * pointerName;
Or
ClassName * pointerName = new ClassName;

  • 建立物件

new ClassName
new ClassName()

class Circle{ public: //存取限制一樣 int redius; int height; double girth; double area; double volume; double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料 return redius * 2 * 3.14159; } int compare(Circle c){ if( redius > c.redius){ return 1; }else if( redius < c.redius){ return -1; }else{ return 0; } } Circle copy(){ Circle c; c.redius = redius; return c; } };
#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle * c1; Circle * c2 = new Circle; // 合法宣告、建立物件 c1 = new Circle(); //合法宣告、建立物件 cout << c1 << endl; cout << c2 << endl; return 0; }

這邊代表新增了兩個物件分別創建出一個記憶體位址

  • 指派

pointerName = memory address;

Ex:
pointerName = new ClassName;
Or
pointerName = &objectName;
Or
pointerName = otherPointerName;

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle * pc1; Circle * pc2 = new Circle; pc1 = new Circle(); cout << pc1 << endl; // 0x136e068d0 cout << pc2 << endl; // 0x136e068b0 cout << "------------------" << endl; delete pc2; // 釋放記憶體 pc1 = pc2; // 重新指派 cout << pc1 << endl; // 0x136e068b0 cout << pc2 << endl; // 0x136e068b0 cout << "------------------" << endl; Circle c; Circle * pc3 = &cc; cout << pc3 << endl; // 0x16fd5b338 cout << &c << endl; // 0x16fd5b338 return 0; }
  • 圖表解釋

  • 成員存取

pointerName -> dataMember
pointerName -> memberFunction()

用 指標 -> 成員 的方式,存取物件成員。

#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle * pc1 = new Circle(); pc1 -> redius = 10; // 指標透過 -> 選取資料成員 cout << pc1 -> getGirth() << endl; //指標透過 -> 呼叫成員函式 return 0; }
  • 物件指標參數

函式原型 : void function(ClassName* pointer)
呼叫敍述 : function(memory address)

  • 物件指標返回值

函式原型 : ClassName * function()
呼叫敍述 : ClassName * pointer = function();


抽象

  • [
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    ] 持續更新

封裝

物件導向中用來實作資訊隱藏的機制,確保物件的安全。其作法為:隱藏不想讓外界碰觸的成員,只公開接受外界存取的成員。
一般而言,類別開發者為了物件的安全,會把資料成員全部隱藏起來,只公開存取或運算資料成員的函式成員。讓物件資料的修改,完全由函式成員掌控其值的變化,避免物件產生預期外的值。
成員存取的控制以存取修飾詞宣告的段落為單位。在宣告某一個存取修飾詞之後,另一個存取修飾詞出現之前為一個段落。若都沒有宣告,預設為 private 。

  • 存取修飾詞:

1.private
:只允許本類別中的其他成員存取。
2.protected
:允許本類別的子類別存取。
3.public
:允許所有類別存取。

#define PI 3.14159 class Circle{ private: int radius; int height; public: double getGirth(); double getArea(); double getVolume(); void setRadius(int r); void setHeight(int h); }; double Circle :: getGirth(){ return radius * 2 * PI; } double Circle :: getArea(){ return radius * radius * PI; } double Circle :: getVolume(){ return getArea() * height; } void Circle :: setRadius(int r){ if(r >= 1 && r<= 1000){ radius = r; } } void Circle :: setHeight(int h){ if(h >= 1 && h<= 100){ radius = h; } }
#include <iostream> using namespace std; #include "Circle.h" int main(){ Circle c1; c1.setRadius(10); c1.setHeight(5); cout << c1.getArea() << endl; }

繼承

物件導向中用來避免程式碼重複撰寫與減低維護困難度的機制。子類別可以繼承父類別中的所有成員。也就是說,把多個類別都會用到的成員,寫在父類別中,子類別只要繼承父類別,不必再寫,就可以擁有那群成員。不但避免了程式寫的重複撰寫,也減少了日後維護的困難度。因為若有程式碼要修改,只需要修改一個類別。

當我們寫一個新類別,指定繼承某一個或多個己存在的類別時,就是在使用那一個或多個己存在的類別。新類別稱之為衍生類別 (Derived class) 或子類別 (Child class),己存在的類別稱之為基礎類別 (Base class) 或父類別 (Parent class)。子類別繼承父類別中的所有成員。但繼承了不代表可以存取,子類別不能存取父類別中宣告為 private 的成員,只能存取宣告為 protected 或 public 的成員。

當你是類別開發者時,不接受子類別與物件存取的,宣告為 private。不接受物件存取,但接受子類別存取的,宣告為 protected。接受物件存取的才宣告為 public。

#include <iostream> using namespace std; #include "myheader.h" int main(){ Circle c1; cout << c1.getArea() << endl; cout << c1.getGirth() << endl; return 0; }
class Shape{ int area; //成員變數 int girth; public: Shape(){ //建構函式 area = 0; girth = 0; } int getArea(); //成員函數 int getGirth(); }; int Shape ::getArea(){ //定義成員函式 return area; } int Shape ::getGirth(){ return girth; } class Circle : public Shape{ //子類別Circle(下層)繼承父類別Shape(上層) };
  • 繼承運算符號

  • 繼承是為了在父類別的基礎之上,做進一步的開發。所以繼承不是重點,重點是繼承後的子類別可以做什麼?

  • 子類別中可以:

  1. 新增子類別的成員
  2. 在子類別中可以新增父類別中沒有宣告的成員。

隱藏父類別的成員
在子類別中可以宣告父類中己宣告過的成員,新宣告的成員會遮蔽父類別中相同名稱的成員。
若子類別中宣告與父類別中名稱相同的資料成員,子類別中的函式存取的是子類別中的資料成員。但父類別中的函式存取的是父類別中的資料成員。

若子類別中宣告與父類別名稱相同但參數列不同的函式,不是 Overloading,一樣是遮蔽了父類別的成員函式。

#include <iostream> using namespace std; #include "myheader.h" int main(){ Circle c1; c1.setRedius(10); cout << c1.getArea()<< endl; cout << c1.getGirth() << endl; cout << "-----------------" << endl; return 0; }
#define PI 3.14159 class Shape{ protected: //為了讓子類可以改變父類中的成員函數 int area; //成員函數 int girth; public: Shape(){ // 建構函式 area = 0; girth = 0; } int getArea(); int getGirth(); }; int Shape ::getArea(){ //定義成員函式 return area; } int Shape ::getGirth(){ return girth; } class Circle : public Shape{ //繼承 private: int redius; public: Circle(){ //新增建構函式避險出現例外的值的錯誤 redius = 0; } void setRedius(int r); }; void Circle :: setRedius(int r){ if ( r >= 0 && r <= 100 ){ redius = r; area = redius * redius * PI ; } }
  • 透過新增一個專門給子類讀取的成員函數
#define PI 3.14159 class Shape{ private: int area; int girth; protected: void setArea(int a); // 設定一個成員函式可以給子類讀取 void setGirth(int g); public: Shape(){ area = 0; girth = 0; } int getArea(); int getGirth(); }; int Shape ::getArea(){ return area; } int Shape ::getGirth(){ return girth; } void Shape :: setArea(int a){ // 設定該成員函式中的值丟給private area = a; } void Shape :: setGirth(int g){ // 設定該成員函式中的值丟給private girth = g; } class Circle : public Shape{ private: int redius; public: Circle(){ redius = 0; } void setRedius(int r); }; void Circle :: setRedius(int r){ if ( r >= 0 && r <= 100 ){ redius = r; setArea(redius * redius * PI); // 呼叫protected } }

主要必須記得要讓子類碰的不是成員變數,而是成員函式。這樣的寫法比較正確,確保製作出來的類別符合封裝的原則


建構函式

物件建立時,環境會在配置記憶體之後呼叫類別中定義的建構函式。類別開發者會在建構函式中撰寫物件初始化的程式碼,一般是指定資料成員的初值。建構函式的名稱必須與類別名稱一樣,可以有參數列,但不可以有返回型別。參數列可以宣告參數預設值。

  1. 預設的建構函式
    建立物件時,若建立物件的敍述中沒有指定建構函式。環境預設會呼叫沒有參數的建構函式。所以沒有參數的建構函式就是預設的建構函式。
    類別中若沒有撰寫建構方法,編譯器會自動產生沒有參數的建構方法。但是,自動產生的建構方法並不會自動指派資料成員的初值,所以建構出來的物件是不安全的。而且,若類別中有撰寫任何建構方法,編譯器就不會自動產生沒有參數的建構方法。
  1. Overloading 建構函式
    類別中除了沒有參數的建構函式,也可以視需求,Overloading 多個建構函式。
  1. this
    寫法: this -> 建構函式中指向的方法
    每一個成員函式中都有一個隱含的指標 this。this可以指向呼叫此函式的物件,用來取存物件成員。
    主要用途有二:
    在專業編輯環境中快速找到要存取的成員
    解決成員變數與參數名稱重複的問題。
  1. 子類別的建構函式指定呼叫父類別的建構函式
    若父類別沒有預設的建構函式或 Overloading 多個建構函式,子類別的建構函式可以指定呼叫。

多型

  • [
    Image Not Showing Possible Reasons
    • The image file may be corrupted
    • The server hosting the image is unavailable
    • The image path is incorrect
    • The image format is not supported
    Learn More →
    ] 持續更新

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →
下一章> CMake?如何撰寫CMackLists.txt?Makefile?