owned this note
owned this note
Published
Linked with GitHub
![](https://hackmd.io/_uploads/rkx6gvyAn.png)
# 什麼是C/C++?
* 跨平台語言
* Bjarne Stroustrup 開發
* 系統資源和內存高度控制
* 可移植性,應用在多個平台
* C++是C語言的延伸
* 函式導向 vs 物件導向
## 怎麼編譯運行?
可以參照這一篇
https://hackmd.io/@Chunghao/rJBkfDQ1T
---
## C基礎語法
**針對基礎輸出輸入**
```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之前 | 無號 十進位整數 | 浮點數(指數) |
:::danger
C語言必須要使用,C++則沒有必要使用
:::
## C++基礎語法
**針對基礎輸出輸入**
```cpp=
#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; 差別**
```cpp=
#include <iostream>
int main() {
std::cout <<"Hello World" ;
return 0;
}
```
:::success
> 1. using namespace std;拿掉
> 2. **std::** 語法上可以取代
:::
**\n 與 endl 差異**
```cpp=
#include <iostream>
int main() {
std::cout << "Hello World " << std::endl;
return 0;
}
```
---
## 變量
```cpp=
#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為有效字元
* 可以使用英文字母,數字,底線
* 名稱中間不能有空白
* 第一個字元不可為數字
* 名稱要有意義,長短適中
* 大小寫有別
### 常量
```cpp=
#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=a*b -> a*=b|a=a/b -> a/=b |a=a%b -> a%=b|
```cpp=
#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++運算輸出練習
---
```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運算輸出練習
:::warning
可以看得出來C/C++語法上有些許上的差異。
:::
### 邏輯運算子
| && | || | ! |
| -------- | -------- | -------- |
| AND or 且 | OR or 或 | NOT or 否 |
### 遞增/減運算子
| i++ | i- - | ++i | - -i |
| -------- | -------- | -------- |-------- |
| 表示先輸出i,再對i做出加法 | 表示先輸出i,再對i做出減法 | 先對i進行加法,再輸出i | 先對i進行減法,再輸出i
---
## 資料型態轉換
```cpp=
#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++資料型態的轉換
```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語言沒有查閱型別的函式庫
## 數學運算
:::warning
1. C語言中使用:#include <math.h>
2. C++語言中使用:#include <cmath>
:::
```cpp=
#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;
}
```
**結果回傳:**
```cpp=
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++撰寫數學函式庫
---
```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;
}
```
**結果回傳:**
```c=
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撰寫數學函式庫
:::info
C語言沒有max,min的函數,因此要使用這種比大小方法回傳最大值。
```c=
result = (x > y) ? x : y; // 返回為較大的數
printf("max(5, 10) = %d\n", result);
```
:::
---
## **#define**是什麼?
:::success
* C語言能用的,C++都可以做使用。
* 宏定義命令,由以下規範所組成 :+1:
* 簡單的宏定義如下:
```cpp
#define <宏名> <字符串>
```
* 帶有參數的宏定義:
```cpp
#define <宏名> (<參數表>) <宏體>
```
:::
1. 簡單的宏定義:
```cpp=
#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;
}
```
:::warning
> 1. 這個意思是指定義一個簡單的宏定義PI的值為3.14,因此在main當中調用PI就可以代表3.14這個數值。
> 2. Ans:
請輸入半徑:2
圖的面積是:12.56
圖的周長是:12.56
:::
2. 帶有參數的宏定義:
```cpp=
#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;
}
```
:::warning
> 1. 這個意思是指定義一個帶有參數的宏定義SQUARE,其參數x的方法為x*x,在main當中宣告num = 5,帶入SQUARE的方法,再將結果賦值給squared。
> 2. Ans:數字5的平方是25
:::
---
## 條件語句
### 1. if/else
* C語言:
```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;
}
```
:::success
* 這邊使用到條件式,**&number**這個用法代表把值帶入number。
:::
```c=
#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;
}
```
```c
*
**
***
****
*****
```
:::success
i=層數, j=*數,C++基本上只有輸出入的部分改掉。
:::
* C++語言:
```cpp=
#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;
}
```
:::warning
* 這邊使用 " cin >> number" 把值帶入number,基本上與C大同小異。
:::
:::success
* 值得一提的是C++語法當中有一個有趣的寫法(Cpp專屬條件表達式,雜項運算符),如下:
```cpp=
#include <iostream>
using namespace std;
int main() {
int number = 20;
string result = (number < 18) ? "goodmorning":"hi";
cout << result << endl;
return 0;
}
```
* :zap: Condition ? X : Y
```cpp
string result = (number < 18) ? "goodmorning":"hi";
```
:::
---
### 2. switch case
* C語言:
```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++語言:
```cpp=
#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語言:
```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++語言:
```cpp=
#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;
}
```
:::info
**while(ture)** or **while(1)**,都代表為真的意思,都會執行無限迴圈。
:::
---
## 陣列
* C語言:
```c=
#include <stdio.h>
#include <string.h>
int main() {
char timer[][15] = {"jack", "chris", "anderson", "kevin"};
printf("%s\n", timer[2]);
return 0;
}
```
:::info
* 這邊代表定義一個timer二維陣列,每個陣列的數組限制大小為15,從0開始數到2,其輸出為**anderson**
* 值得注意的是,C語言當中,並沒有**string**的用法,字串使用**char**
:::
* C++語言:
```cpp=
#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. 靜態變數
#### 介紹
> 一段程式碼,執行特別的工作。
> 可重複使用
> 讓程式變得淺顯易懂
> 在呼叫函式不變的狀況下,可以直接修改程式。
#### 宣告
```cpp
回傳值型態 函式名稱 (參數)
```
* 沒有回傳值:void square(int length)
```c=
#include <stdio.h>
void square(int length); //宣告function 沒有回傳值
void square(int length){
//撰寫function內容
}
int main(){
square(); //傳入square function的參數
}
```
* 有回傳值:int square(int len)
```c=
#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中的參數
```c=
#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 (區域變數)只能在部分函式中使用
> 範例如下:
```c=
#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變數,是一個區域變數,但不會因為函式執行結束,變數內的資料就不見。
```c=
#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
:::info
指標是用來儲存"**記憶體位址**"的變數,設計師可以透過指標來管理記憶體,包含:**配置**,**存值**,**取值**,**釋放**。
:::
> 1. & : 取得已存在的變數位址
> 2. * : 表示為指標變數
> 3. new : 配置指定型別的記憶體位址
> 4. 另一個指標:將已經存在的指標所儲存的記憶體位址指派給另一個指標
### 宣告
```cpp=
type* name; // *就代表指標 這個指標為name
type* name = address;
type* nameA,* nameB;
```
* 基本存取功能
```cpp=
#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 的用法
```cpp=
int * r;
r = new int; //另外派一個空間給r
*r =100; //管理空間裡的值
cout << r << endl; //得到r的位址
cout << *r << endl; //得到該位址中的值
return 0;
```
* 另一個指標 的用法
```cpp=
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釋放
```cpp=
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;
```cpp=
int * const m = new int ;
*m =100;
*m =200;
cout << *m <<endl; //200
```
* 如果這邊再寫delete釋放掉m的位址
#### type * const name;
```cpp=
int * const m = new int ; //配了一個新的位址給m
*m =100; //指標管理裡面的值
*m =200; //可以二次更改裡面的值
cout << *m <<endl;
delete m;
m = new int ;
return 0;
```
:::warning
```
error: cannot assign to variable 'm' with const-qualified type 'int *const'
```
> 因此可得出const 只可讀取
:::
#### const type * name;
```cpp=
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;
```
:::warning
```
error: read-only variable is not assignable
```
> 這樣寫n還沒定義值就先報錯無改給值,你可以得很奇怪,因此這邊不能這樣賦予他一個新的記憶體空間。
:::
```cpp=
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
```cpp=
#include <iostream>
using namespace std;
void showValue(int * p);
void showValue(int * p){
cout << *p << endl;
}
```
* main2.cpp
```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;
}
```
* 解釋
![](https://hackmd.io/_uploads/SyKiNlQ0h.png)
---
#### 為何Value,&Value輸出結果不一樣?
* main3.h
```cpp=
#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
```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
![](https://hackmd.io/_uploads/BJqFoe7A2.png)
> 解釋第二部分為何輸出=101
![](https://hackmd.io/_uploads/H1Qm3x7Ch.png)
---
### return 型別為指標
> 呼叫敘述是一個記憶體位址
> 不應return 區域變數的記憶體位址
> 可以宣告為const
* main3.h
```cpp=
#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
```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;
```cpp=
#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
```cpp=
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;
```
:::warning
* 解決辦法
```cpp=
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. 存取
```cpp=
#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;
}
```
- [:negative_squared_cross_mark: ] 動態指標陣列
# 物件導向
## 物件
* 何謂物件?
> 執行環境依照類別中的宣告,所配置的記憶體群組。它可以儲存一群資料,而該群資料可以完整的描述一個特定的運算單位。例如:一群可以完整描述一張訂單的資料、一群可以完整描述購買者的資料、一群可以完整描述一件商品的資料、一群可以完整描述日期時間的資料、一群可以完整描述信用卡付款的資料...。
> 也就是說:一個記憶體群組,代表一個特定運算單位的完整資料。這一個記憶體群組,我們就稱之為物件。
> 所以就程式語言的角度上也可以說:物件一群記憶體的集合。
> 物件擁有什麼
資料成員(Data Members) => 儲存資料(變數)。
很多文件的解說中稱之為屬性或狀態。在 Java 中稱之為欄位。
> 成員函式 (Member Functions) => 運算資料。
很多文件的解說中稱之為行為或能力。在 Java 中稱之為方法。
* 為什麼要使用物件導向
在物件導向之前是函式導向。隨著運算複雜度的提升,各語言也逐一的支援物件導向。物件導向與函式導向的基本差異是:物件儲存資料,物件運算資料。
* 以 C / C++ 語言為例,在函式導向時期(C 語言時期),雖然可以用結構(struct)宣告一個新型別,集合一群資料。但是,那一群資料本身並不具有運算能力,它只能當參數讓函式運算。所以,資料與運算資料的函式是沒有關係的。
* 到了物件導向時期(C++ 時期),改用類別(class)來宣告新型別。依照類別的宣告建立的物件,除了是一群資料的集合之外,本身也具有運算的能力。也就是說:資料與運算資料的函式是屬於同一個物件的成員。
* 相同類別的物件,一定擁有相同的成員
相同名稱的資料成員,但它的值不見得一樣。
相同名稱的成員函式,但它運算的結果不一定一樣。
每一個物件獨立管理與運算自己的資料
除非特殊的設計需求。基本上,每個物件運算自己的資料。
## 函式導向 & 物件導向 差異
* 函式導向
```cpp=
//函式導向,函式是老大
struct boot{
int redius;
int height;
double girth;
double area;
double volume;
};
double getGirth(struct boot c); //結構指的是接受一群資料做運算
```
* 物件導向
```cpp=
// 物件導向,物件是老大,物件擁有資料,且擁有運算能力
class Circle{
public: //存取限制一樣
int redius;
int height;
double girth;
double area;
double volume;
double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料
return redius * 2 * 3.14159;
}
};
```
* 執行物件導向
```cpp=
#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++ 中合法的型別。
* 為什麼要開發類別
> 因為程式有物件的需求,所以依照物件的需求開發類別
* 類別用來做什麼
> 宣告變數
宣告指標
宣告參考
物件轉型
物件識別
執行環境製做物件的說明書
* 類別中有什麼
> 物件成員=> 物件資料成員,物件成員函式
類別成員=> 類別資料成員,類別成員函式
建構函式(建構子)
其他
```cpp=
/*
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;
> 變數宣告時,環境會依照類別中的宣告來建立物件。並把物件的記憶體位址指派給變數。
```cpp=
class Circle{
public: //存取限制一樣
int redius;
int height;
double girth;
double area;
double volume;
double getGirth(){ //定義,運算自己的資料 , 物件的函式運算自己的資料
return redius * 2 * 3.14159;
}
};
```
```cpp=
#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
![](https://hackmd.io/_uploads/Syu9zXLC3.png)
> 到這裡,執行main函式,radius記憶體位址變數變成10,並且執行getGirth,再將結果複製給0x123(c1)
![](https://hackmd.io/_uploads/rk4MNmIRn.png)
> 接著main函式當中,執行第二個c2,為何執行第二個c2,原本的記憶體位址不會消失呢?由下圖解釋:
![](https://hackmd.io/_uploads/SkxobzPA3.png)
* 成員存取
> objectName.dataMember
objectName.memberFunction()
```cpp=
#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;
```cpp=
#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);
> 在函式呼叫敍述的小括號中放物件變數時,是把本物件的成員變數的值,複製給函式的物件變數參數。
```cpp=
#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;
}
```
```cpp=
// 物件導向,物件是老大,物件擁有資料,定且擁有運算能力
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
![](https://hackmd.io/_uploads/BJc-tzPAn.png)
* 物件變數返回值
> 函式原型 : ClassName function()
呼叫敍述 : ClassName objectName = function();
```cpp=
#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;
}
```
```cpp=
// 物件導向,物件是老大,物件擁有資料,定且擁有運算能力
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;
}
};
```
> 代表返回值是這個類別的物件變數
> 不過這樣有個壞處,這個寫法會多佔用一個記憶體位址,如果是大型運算會佔太多不必要的空間
## 物件指標
* 宣告
:::info
> ClassName * pointerName;
Or
ClassName * pointerName = new ClassName;
:::
* 建立物件
:::warning
> new ClassName
new ClassName()
:::
```cpp=
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;
}
};
```
```cpp=
#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;
:::danger
> Ex:
pointerName = new ClassName;
Or
pointerName = &objectName;
Or
pointerName = otherPointerName;
:::
```cpp=
#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;
}
```
* 圖表解釋
![](https://hackmd.io/_uploads/BJ5SJUuA3.png)
* 成員存取
> pointerName -> dataMember
pointerName -> memberFunction()
:::warning
> 用 指標 -> 成員 的方式,存取物件成員。
:::
```cpp=
#include <iostream>
using namespace std;
#include "Circle.h"
int main(){
Circle * pc1 = new Circle();
pc1 -> redius = 10; // 指標透過 -> 選取資料成員
cout << pc1 -> getGirth() << endl; //指標透過 -> 呼叫成員函式
return 0;
}
```
* 物件指標參數
:::warning
> 函式原型 : void function(ClassName* pointer)
呼叫敍述 : function(memory address)
:::
* 物件指標返回值
:::info
> 函式原型 : ClassName * function()
呼叫敍述 : ClassName * pointer = function();
:::
---
## 抽象
- [ :negative_squared_cross_mark:] 持續更新
---
## 封裝
> 物件導向中用來實作資訊隱藏的機制,確保物件的安全。其作法為:隱藏不想讓外界碰觸的成員,只公開接受外界存取的成員。
一般而言,類別開發者為了物件的安全,會把資料成員全部隱藏起來,只公開存取或運算資料成員的函式成員。讓物件資料的修改,完全由函式成員掌控其值的變化,避免物件產生預期外的值。
成員存取的控制以存取修飾詞宣告的段落為單位。在宣告某一個存取修飾詞之後,另一個存取修飾詞出現之前為一個段落。若都沒有宣告,預設為 private 。
* 存取修飾詞:
> 1.private
:只允許本類別中的其他成員存取。
> 2.protected
:允許本類別的子類別存取。
> 3.public
:允許所有類別存取。
```cpp=
#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;
}
}
```
```cpp=
#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。
```cpp=
#include <iostream>
using namespace std;
#include "myheader.h"
int main(){
Circle c1;
cout << c1.getArea() << endl;
cout << c1.getGirth() << endl;
return 0;
}
```
```cpp=
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,一樣是遮蔽了父類別的成員函式。
```cpp=
#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;
}
```
```cpp=
#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 ;
}
}
```
* 透過新增一個專門給子類讀取的成員函數
```cpp=
#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. 預設的建構函式
建立物件時,若建立物件的敍述中沒有指定建構函式。環境預設會呼叫沒有參數的建構函式。所以沒有參數的建構函式就是預設的建構函式。
類別中若沒有撰寫建構方法,編譯器會自動產生沒有參數的建構方法。但是,自動產生的建構方法並不會自動指派資料成員的初值,所以建構出來的物件是不安全的。而且,若類別中有撰寫任何建構方法,編譯器就不會自動產生沒有參數的建構方法。
> 2. Overloading 建構函式
類別中除了沒有參數的建構函式,也可以視需求,Overloading 多個建構函式。
> 3. this
寫法: this -> 建構函式中指向的方法
每一個成員函式中都有一個隱含的指標 this。this可以指向呼叫此函式的物件,用來取存物件成員。
主要用途有二:
在專業編輯環境中快速找到要存取的成員
解決成員變數與參數名稱重複的問題。
> 4. 子類別的建構函式指定呼叫父類別的建構函式
若父類別沒有預設的建構函式或 Overloading 多個建構函式,子類別的建構函式可以指定呼叫。
---
## 多型
- [ :negative_squared_cross_mark:] 持續更新
---
:ghost: 下一章--> **CMake?如何撰寫CMackLists.txt?Makefile?**