# C++ 教學講義
###### tags: `tutor` `C++`
本文所有內容與資料皆由本人蒐集與撰寫,轉載請註明出處。
- [C++ 練習題](https://hackmd.io/Qw8rHQV_RFqJ3Dj9A6oeKw?view)
- 亦可參考 [ZeroJudge](https://zerojudge.tw/Problems)、[Leetcode](https://leetcode.com/problemset/all/)
## 基本語法
- 參見 [C++ 語言自學手冊](https://cpp.enmingw32.dev/),此不贅述
```cpp=
#include<iostream>
using namespace std;
int main(){
// Your program starts here
// Variable Types
int a = 0;
char b = 'b';
float c = 3.14;
double d = 3.1415;
bool e = true;
// Basic Input & Output
cin >> a;
cout << "hello world";
// Control Flow
if (a == 0){
cout << "a is 0\n";
}
else if (a > 0){
cout << "a is greater than 0\n";
}
else{
cout << "a is less than 0\n";
}
switch (a) {
case 1:
cout << "a = 1";
break;
case 2:
cout << "a = 2";
break;
default:
cout << "a != 1 && a != 2";
break;
}
// Loops
// Both have continue & break
int N = 10;
for (int i = 0; i < N; i++){
cout << "Loop " << i << endl;
}
int i = 0;
while (i < N):{
cout << "Loop " << i << endl;
i++;
}
// Your program ends here
return 0;
}
```
## 指標(Pointers)
指標是一個強大的工具,用於指向變數,或直接操作記憶體位置,也是 Python 中沒有的一種變數類別,但卻是個很重要的概念。指標能夠讓我們間接引用變數和物件,並在動態記憶體配置和資料結構中皆發揮了重要的作用。透過指標我們可以訪問和修改記憶體中的數據,提供更高的靈活性和效能。然而,正確使用指標很重要,因為錯誤的操作可能導致記憶體問題,像是存取到未分配的空間等等。
先介紹兩個運算子:
- &:Address-of Operator, 取址運算子,用以取出變數所在之記憶體位址
- \*:Dereference Operator, 取值運算子,用以取出變數所指向位置之值
> & 與 \* 可以互相抵銷,另外 \* 同時也是乘法運算元,要小心喔!
搭配以上兩者之使用,我們便可以使用指標:
- 創建指標並指向變數(我們通常將 int* 直接視為一個變數類別)
```cpp=
int a = 10;
int* aPtr = nullptr; // or int *aPtr = nullptr;
aPtr = &a;
*aPtr = 20; // we can change the value of a variable by pointer
cout << a << endl; // 20
// Any type can be pointed at, for example:
int** aPtrPtr = &aPtr;
class Car{
// Implementation of Class Car
}
Car c;
Car* cPtr = &c;
```
- 指標、變數、* 與 & 的關係
```cpp=
int a = 10;
int* aPtr = &a;
cout << "value of a = " << a << endl; // 10
cout << "value of aPtr = " << aPtr << endl; //0x123450
cout << "address of a = " << &a << endl; //0x123450
cout << "address of aPtr = " << &aPtr << endl; //0x543210
cout << "value of the variable pointed by aPtr = " << *aPtr << endl; // 10
```
<!--  -->
- 動態記憶體配置(DMA)
```cpp=
// for integer
int* ptr = new int;
*ptr = 0
// Remember to do this at the end, to avoid memory leak!
delete ptr;
ptr = nullptr;
```
```cpp=
// for integer array
int N;
cin >> N;
int* ptr = new int[N];
for (int i = 0; i < N; i++){
ptr[i] = 0;
}
// Remember to do this at the end, to aviod memory leak!
delete [] ptr;
ptr = nullptr;
/*
Don't do this, this is for static memory allocation
int N = 10;
int arr[N];
*/
```
> 延伸閱讀:[Why must we use pointer during dynamic memory allocation in C++?](https://qr.ae/pyFiAo)
- 應用:swap() 實作
- 錯誤示範(Recap:區域變數與全域變數)
```cpp=
void badSwap(int a, int b) {
int t = a;
a = b;
b = t;
}
```
- 正確示範(注意呼叫時要傳入 int* !)
```cpp=
void goodSwap(int *a, int *b) {
int t = *a;
*a = *b;
*b = t;
}
```
- 指標的概念圖

> 延伸閱讀:[圖解說明指標概念](https://husking-studio.com/cpp-pointer-tutorial/)
## 類別(Class)
作為物件導向的程式語言,C++ 的 class 相關語法可以說是非常重要與常用,在大型專案的開發一定少不了他的身影,且多樣又彈性的語法支援,可以說是將物件導向的概念發揮到了極致。此處我們簡述一些常見的相關語法與概念,過於高深或少見的語法我們會先略過。
- 基本語法
```cpp=
class Car {
private:
int wheels;
string plateID;
string driver;
bool engine;
int meters;
public:
Car(string plateID, string driver);
~Car();
void turnOnEngine();
bool checkEngine();
void drive(int distance);
void turnOffEngine();
void whoisDriving();
int getMeters();
};
Car::Car(string plateID, string driver) {
this->wheels = 4;
this->plateID = plateID;
this->driver = driver;
this->engine = false;
this->meters = 0;
this->turnOnEngine();
}
Car::~Car() {
// This is a Destructor
// You can and you should delete dynamically allocated memory here
}
void Car::turnOnEngine() {
if (this->checkEngine()) {
this->engine = true;
cout << "Engine Started!" << endl;
}
}
bool Car::checkEngine() {
return true;
}
void Car::drive(int distance) {
if (engine) {
this->meters += distance;
cout << "Drive " << distance << " kilometers." << endl;
}
else {
cout << "Engine is not turned on." << endl;
}
}
void Car::turnOffEngine() {
this->engine = false;
cout << "Engine has been turned off." << endl;
}
void Car::whoisDriving() {
cout << this->driver << " is driving the car." << endl;
}
int Car::getMeters() {
return this->meters;
}
```
從上面的例子中,我們可以發現幾點與 Python 較不同的地方:
- 甚麼是 private、public?
- Constructor 與 Desturctor 在哪裡?
- self 變成 this 了?
- 為何所有 function 前面都有 Car::?
別擔心,我們一樣一樣來看。
### Private、Public、Protected
在C++中,private、public 和 protected 是用來定義類別中成員的可存取性(accessibility)的關鍵字,它們決定了這些成員在類別的內部和外部的可見性和可存取性。
- Private:僅限類別內部成員存取,不可透過外部存取(像是從外面寫 c.wheels 就是非法操作會報錯,必須透過額外的 getter 與 setter 才能對其進行操作),不會被繼承
- Protected:與 Private 類似,唯一不同是會被繼承
- Public:可以任意從外部取用,無任何限制
簡而言之,如果目前還沒有複雜的繼承需求,就簡單使用 public 與 private 區分即可!不想讓別人操作到的就用 private,沒差的就用 public!
> [參考:C++ public, protected, private 總和比較整理](https://www.wongwonggoods.com/all-posts/cplusplus/cpp-concept/c-public-protected-private/)
> [延伸:C++ public、protected、private 和 friend](https://blog.csdn.net/a3192048/article/details/82191795)
#### Getter & Setter
為了要存取與修改 Private member variable,我們會使用 Getter & Setter 來幫助我們。
- Getter
* 用於獲取變數的值
* 通常公開的方法,通常以 get 為前綴,後接要獲取的屬性名稱
- Setter
* 用於設定變數的值
* 通常公開的方法,通常以 set 為前綴,後接要設定的屬性名稱,並會接受參數以更新屬性的值
一個簡單的例子:
```cpp=
class MyClass {
private:
int myValue;
public:
int getValue() const {
return myValue;
}
void setValue(const int& newValue) {
this->myvalue = newValue;
}
};
```
> 延伸問題:既然如此,為何不乾脆直接改成 Public 就好?
> 參考解答:因為透過 Getter & Setter,我們可以更好的控制 Private Member Variable 的獲取與修改,進而避免不想要或意外的情況產生
#### 封裝(Encapsulation)
上述的 Getter & Setter,其實就是實現了物件導向程式設計(OOP)中「封裝」的概念。([複習:OOP 三大精隨](https://hackmd.io/w5n1Ow8NSea_-UAeXTJDSw#OOP-%E4%B8%89%E5%A4%A7%E7%B2%BE%E9%9A%A8---%E5%B0%81%E8%A3%9D%E3%80%81%E7%B9%BC%E6%89%BF%E3%80%81%E5%A4%9A%E5%9E%8B%EF%BC%88%E8%A3%9C%E5%85%85%EF%BC%89))
將物件的內部狀態和行為隱藏在物件內部,只公開必要的方法給外界使用。封裝可以保護物件免於外界的非法存取,並且讓物件更容易維護和修改。
```cpp=
class Animal {
private:
string private_name; // This cannot be accessed from the outside
public:
string public_name; // This can be accessed from the outside
void setName(const string& name) {
this->private_name = name; // Setter
}
};
```
### Constructor & Destructor
constructor(建構函式) 與 destructor(解構函式) 是 class 中的兩種特別函式,當主程式中產生某 class 的物件時,該 class 的建構函式即會自動執行;而當物件生命周期結束,則在物件消滅前會自動執行解構函式。
其相應語法如下:
```cpp=
Car::Car(string plateID, string driver) {
this->wheels = 4;
this->plateID = plateID;
this->driver = driver;
this->engine = false;
this->meters = 0;
this->turnOnEngine();
}
Car::~Car() {
// This is a Destructor
// You can and you should delete dynamically allocated memory here
}
```
> 參考:[Constructor & Destructor](http://cpp2015.blogspot.com/2015/07/constructor-destructor.html)
### This
C++ 中的 this 就與 Python 中的 self 相同意思,但不同於 Python 的是,我們不需要將 this 寫在 member function 的第一個參數位置,就能夠直接使用。
- `self.` (python) <-> `this->` (C++)
事實上,當我們創建一個類別指標,並讓該指標指向一個實體物件後,也可以使用 `->` 來存取該物件的屬性或方法,如下所示。
```cpp=
Car mycar;
Car* carPtr = &mycar;
cout << mycar.driver << endl;
cout << carPtr->driver << endl;
```
### 成員函式的宣告與定義(Car::)
一般來說,我們會將函數的實作(Implementation)與函數宣告(Declaration)寫在一起,像是:
```cpp=
Class Test{
public:
Test(){
cout << "Constructor!" << endl;
}
void hello(){
cout << "Hello!" << endl;
}
~Test(){
cout << "Destructor!" << endl;
}
};
```
不過此種寫法的壞處是,當我們成員函式太多時,整個 Class 會變得很常難以閱讀,因此我們也可以把實作部分(Implementation)拉出來寫,但為了要避免混淆,必須加上該函式的範籌說明(Test::):
```cpp=
Class Test{
public:
Test();
void hello();
~Test();
};
Test::Test(){
cout << "Constructor!" << endl;
}
void Test::hello(){
cout << "Hello!" << endl;
}
Test::~Test(){
cout << "Destructor!" << endl;
}
```
## More on Class:繼承與多型
可參考之前講過的 [OOP 三大精隨](https://hackmd.io/w5n1Ow8NSea_-UAeXTJDSw#OOP-%E4%B8%89%E5%A4%A7%E7%B2%BE%E9%9A%A8---%E5%B0%81%E8%A3%9D%E3%80%81%E7%B9%BC%E6%89%BF%E3%80%81%E5%A4%9A%E5%9E%8B%EF%BC%88%E8%A3%9C%E5%85%85%EF%BC%89)。
### 繼承(Inheritance)
子類別可以繼承父類別的屬性和方法,並且可以擴展或覆寫父類別的行為。繼承可以提高程式碼重複使用性,並且可以讓類別之間建立階層關係,方便對類別進行分類和管理。
```cpp=
class Animal {
protected:
string name;
public:
Animal(const string& name) {
this->name = name;
}
virtual void walk() const {
cout << "walking" << endl;
}
virtual void eat() const {
cout << "eating" << endl;
}
};
class Dog : public Animal {
public:
Dog(const string& name) {
this->name = name + " the dog";
} // Constructor will not be inherited
};
int main() {
Dog A("A");
A.walk();
A.eat();
cout << A.name << endl;
return 0;
}
```
```
Output:
walking
eating
A the dog
```
#### 繼承方法
上述的繼承使用 `class Dog : public Animal` 這行,這邊有個 keyword `public` 出現,這個 keyword 代表這邊使用的方法為 `public` 繼承方法,依照不同的繼承方法與不同的父類別可見度,會產生如下的表格:

這表格我認為參考就好,不用記下來,搞不清楚的話就都用 `public` 繼承方法即可。
### 多型(Polymorphism)
同樣的方法名稱可以在不同的類別中有不同的實現方式,這稱為多型。多型可以讓程式碼更加靈活,並且可以讓不同的物件對相同的方法有不同的行為。多型可以通過繼承和介面實現,是物件導向設計中非常重要的概念。
```cpp=
class Animal {
protected:
string name;
public:
Animal(const string& name) {
this->name = name;
}
virtual void walk() const {
cout << "walking" << endl;
}
virtual void eat() const {
cout << "eating" << endl;
}
};
class Dog : public Animal {
public:
Dog(const string& name) {
this->name = name + " the dog";
}
void walk() const override {
cout << name << " is using foot to walk" << endl;
}
void eat() const override {
cout << name << " is eating bone" << endl;
}
};
class Duck : public Animal {
public:
Duck(const string& name) {
this->name = name + " the duck";
}
void walk() const override {
cout << name << " is using two feet to walk" << endl;
}
void eat() const override {
cout << name << " is eating worm" << endl;
}
};
int main() {
Dog A("A");
Duck B("B");
A.eat();
B.eat();
return 0;
}
```
```
Output:
A is eating bone
B is eating worm
```
### 相關補充
老實說我覺得這邊都太複雜了,但為了課程完整性還是簡單提及,除非你是 C++ 老手,不然根本不可能記得,所以我的建議是先了解概念,有需要再回來查。
- [C++ virtual 的兩種用法](https://shengyu7697.github.io/cpp-virtual/)
- [C++ 中的 Overload、Override 和 Overwrite](https://note.artchiu.org/2021/04/01/c%E4%B8%AD%E7%9A%84overload%E3%80%81override%E5%92%8Coverwrite/)
- [C++ const 的三種用法與範例](https://shengyu7697.github.io/cpp-const/)
## 延伸主題:Call by value / address / reference / assignment
- [C++ call by value, call by address (pointer), call by reference ](https://www.wongwonggoods.com/all-posts/cplusplus/cpp-concept/cpp-value-address-pointer-reference/)
- [Python 是 Pass By Value, Pass by Reference, 還是 Pass by Sharing?](https://medium.com/starbugs/python-%E4%B8%80%E6%AC%A1%E6%90%9E%E6%87%82-pass-by-value-pass-by-reference-%E8%88%87-pass-by-sharing-1873a2c6ac46)
## 延伸主題:C++ STL
- [C++ STL 全](https://4yu.dev/post/STL/)
- [C++ STL 容器(一) - 基本介紹](https://jasonblog.github.io/note/c++/stl_rong_qi_4e0029_-_ji_ben_jie_shao.html)
- [C++ API / STL 整理](https://hackmd.io/@meyr543/BkgMaiV6Y)