# <div id=animation_title>**C++ Beginner**<div>
###### tags: `C++`
### 參考資料
- [Mes's C++ notebook](https://hackmd.io/@Mes/mes_note/https%3A%2F%2Fhackmd.io%2F%40Mes%2FCPP_Note)
- [從零開始的演算法競賽入門教學](https://emanlaicepsa.github.io/2020/10/19/overall/)
- [語言技術:C++ Gossip](https://openhome.cc/Gossip/CppGossip/index.html)
## 變數、邏輯符號、基本語法
### 變數型態
| 型態 | 代表意義 |
|--------|----------------------|
| int | 整數 |
| double | 浮點數 |
| char | 字元 ex : "a"、"@" |
| string | 字串 |
| bool | 布林值 ex : true/false |
| long | 長整數 |
#### int, long, long long的範圍
| 類型 | 最小值 |最大值 |
|---------|----------------|------|
| unsigned int | 0 | 4294967295 (2^32 - 1)|
|int | -2147483648 | 2147483647 (2^31 - 1)|
|unsigned long | 0 | 4294967295 (2^32 - 1)|
|long | -2147483648 | 2147483647 (2^31 - 1)|
|Unsigned long long | 0 | 18446744073709551615 (2^64 - 1)|
|long long |- 9223372036854775808 | 9223372036854775807 (2^63 - 1)|
|unsigned __int64 | 0 | 18446744073709551615 (2^64 - 1)|
|__int64 | -9223372036854775808 | 9223372036854775807 (2^63 - 1)|
## 字元運算
- https://emanlaicepsa.github.io/2020/10/29/0-5/#%E5%AD%97%E5%85%83%E9%81%8B%E7%AE%97
- [ASCII Table](http://kevin.hwai.edu.tw/~kevin/material/JAVA/Sample2016/ASCII.htm)
***字元在運算的時候,將會被視為其ASCII碼!***
```cpp=
'a'+10 = 107; // 因為 ‘a’ 的 ASCII 碼是 97,在運算時便將其視為 97
```
### cout
```cpp=
std::cout << "要輸出的內容" ;
```
### cin
```cpp=
std::cin >> "要輸入的內容" ;
```
### scanf
https://vimsky.com/zh-tw/examples/usage/cpp-programming_library-function_cstdio_scanf.html
### printf
### if - else if - else 條件式
```cpp=
if ( 條件 ) {
如果條件成立時做什麼...;
}
else if ( 條件 ) {
否則如果條件成立時做什麼...;
}
else {
否則做什麼...;
}
```
- 當 { } 內的東西只有一列的時候,可以省略大括號, 看起來會較為美觀,但是初學者建議還是都加上大括號,避免混淆。
```cpp=
if ( 條件 )
如果條件成立時做什麼...;
else if ( 條件 )
否則如果條件成立時做什麼...;
else
否則做什麼...;
```
- if,else能夠以三元運算子表示(a ? b : c)
```cpp=
if(x == true) val =y;
else val =z;
//能夠被寫成
val = x ? y :z;
```
### 基礎邏輯 Logic
<li> > 大於</li>
<li> < 小於 </li>
<li> == 等於 (注意,不是=)</li>
<li> != 不等於</li>
<li> >= 大於等於</li>
<li> <= 小於等於</li>
<li> && 且</li>
<li> || 或</li>
<li> ! 否</li>
### switch
```cpp=
int number = 2;
switch(number) {
case 1:
//要做的事
std::cout << " " ;
break;
case 2:
//要做的事
std::cout << " " ;
break;
}
```
## 迴圈 Loop
### while
```cpp=
while ( 條件式 ) {
只要當條件成立時,就重覆做的事…;
}
```
### for
```cpp=
for ( [A]一開始先做什麼事 ; [B]條件式 ; [D]等C每作完一次,就做什麼事 ) {
[C]當B條件成立時,就重覆做的事…;
}
```
- 輸出陣列常用單行的for迴圈來印出
```cpp=
for(int i=0; i < sizeof(array)/sizeof(*arr); i++){
std::cout << arr[i] << endl;
}
```
- 也可以利用for range的寫法(C++ 11才可以用)
```cpp=
for(int i : arr ) {
std::cout << i << endl;
}
```
## 陣列 Array
### 一維陣列
#### 宣告
C++內可以宣告一個以索引(index)作為識別的資料結構,稱作陣列,能夠用來儲存資料,宣告陣列的方式為 `資料型態 名稱[長度];`
長度必須是個編譯時期的常數,例如:
```cpp=
int number[10]; // 宣告 10 個元素的整數陣列
double score[10]; // 宣告 10 個元素的浮點數陣列
char ascii[10]; // 宣告 10 個元素的字元陣列
```
宣告陣列後,陣列內的元素並不會自動初始化,宣告僅僅是分配了一段記憶體空間給他,並以陣列的名字標記他在哪,此時陣列內元素的值是原本記憶體的殘值。例如:
```cpp=
int a[5];
for (int i = 0; i < 5; i++) cout << a[i] << " ";
輸出:
1 38055040 591560705 35368912 1
```
因此我們需要手動初始化陣列的值,若想在宣告時一次初始陣列全部的元素值,可以如下:
```cpp=
int number[10] = {0};
double score[10] = {0.0};
char ascii[10] = {'\0'};
bool flag[10] = {false};
```
上面的幾個宣告,整數陣列中的元素都會被初始為 0,浮點數陣列則會被初始為 0.0,字元陣列會被初始為空字元('\0'),而 bool 陣列會被初始為 false。
當然,我們也可以指定不同的初始值給陣列,如:
```cpp=
int number[5] = {0, 1, 2, 3, 4};
double score[5] = {87.0, 78.0, 99.5, 69.5, 82.5};
char ascii[5] = {'A', 'B', 'C', 'D', 'E'};
bool flag[5] = {false, true, false, true, false};
```
#### 存取元素
要存取陣列中的元素值時,可以使用下標(Subscript)運算子 [] 加上索引」,索引值由 0 開始,下面這個簡單的程式是個示範:
```cpp=
#include <iostream>
using namespace std;
int main() {
constexpr int LEN = 10;
int number[LEN] = {0};
for(int i = 0; i < LEN; i++) {
cout << number[i] << " ";
}
cout << endl;
for(int i = 0; i < LEN; i++) {
number[i] = i;
}
for(int i = 0; i < LEN; i++) {
cout << number[i] << " ";
}
cout << endl;
return 0;
}
輸出:
0 0 0 0 0 0 0 0 0 0
0 1 2 3 4 5 6 7 8 9
```
陣列在使用時,一定要先得知陣列長度,不可以存取超過陣列長度的記憶體,這會發生無法預期的結果 (因為超出了有定義值的記憶體位址),陣列本身並不知道自己的長度資訊,在上面的範例中,使用了 LEN 來記錄長度,不過,有沒有辦法計算出長度呢?可以使用底下的方式:
```cpp=
#include <iostream>
using namespace std;
int main() {
int number[5] = {0, 1, 2, 3, 4};
int length = sizeof(number) / sizeof(number[0]);
for(int i = 0; i < length; i++) {
cout << number[i] << " ";
}
cout << endl;
return 0;
}
```
陣列索引值由 0 開始不是沒有原因的,陣列名稱儲存了陣列記憶體的首個位置的位址,而索引值表示陣列元素是相對於陣列首個記憶體位址的位移量(offset),位移的量與資料型態長度有關,如果是 int 整數,每次位移時是一個 int 整數的長度,例如在上例中 number[0] 索引值為 0 時,表示位移量為 0,自然就是指第一個元素,而 number[9] 就是指相對於首個元素的位移量為 9。
若在宣告陣列時指定各個索引處的的值,可以不用宣告陣列元素大小,例如:
```cpp=
int number[] = {1, 2, 3};
double weight[] = {0.4, 3.2, 1.0, 4.2};
char ch[] = {'A', 'B'};
```
上面宣告中,number[] 的元素個數會是 3,weight[] 的個數會是 4,而 chs[] 的個數會是 2。
<br>
如果使用 const 或 constexpr 來修飾陣列,每個索引位置就成為唯讀。例如:
```cpp=
constexpr int number[] = {1, 2, 3};
number[1] = 10; // error: assignment of read-only location 'number[1]'
```
另外,陣列無法像vector直接複製。如果要複製陣列,只能一個一個元素複製,如:
```cpp=
constexpr int LENGTH = 5;
int arr1[LENGTH];
int arr2[LENGTH];
...
for(int i = 0; i < LENGTH; i++) {
arr1[i] = arr2[i];
}
```
同樣的,要比較兩個陣列是否相同的話,也是要一個一個元素比較。
### 二維(多維)陣列
#### 宣告
二維(多維)陣列的宣告與一維陣列相似,要兩個(多個)索引值來指定存取陣列元素
```cpp=
int maze[5][10];
```
#### 存取元素
```cpp=
#include <iostream>
using namespace std;
int main() {
constexpr int ROWS = 5;
constexpr int COLUMNS = 10;
int maze[ROWS][COLUMNS];
for(int row = 0; row < ROWS; row++) {
for(int i = 0; i < COLUMNS; i++) {
maze[row][i] = (row + 1) * (i+ 1);
}
}
for(int row = 0; row < ROWS; row++) {
for(int i = 0; i < COLUMNS; i++) {
cout << maze[row][i] << "\t";
}
cout << endl;
}
return 0;
}
輸出:
1 2 3 4 5 6 7 8 9 10
2 4 6 8 10 12 14 16 18 20
3 6 9 12 15 18 21 24 27 30
4 8 12 16 20 24 28 32 36 40
5 10 15 20 25 30 35 40 45 50
```
上面這個程式宣告了 5 列(Row)、10 行(Column)的陣列,第一個 [] 是用來指定存取哪一列,第二個 [] 是用來指定存取哪一行。
二維陣列在宣告的同時也可以指定元素的值,例如:
```cpp=
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
```
從上面這個程式來看,可以清楚地看出 maze[2][3] 中 2 與 3 的意義,maze[2] 表示 maze 有兩個元素,各是 {1, 2, 3} 與 {4, 5, 6},也就是說,這兩個元素是一維陣列,而長度是 3。
其實二維陣列存取時的行與列,只是為了便於理解陣列元素索引,索引值的意義,仍是指相對於陣列第一個元素的位移量。 例如,在一維陣列中的陣列配置與索引意義如下圖所示(若 int 長度為 4 個位元組):

在上面的例子中,二維陣列將得到的記憶體分為兩個區塊,宣告陣列 maze[2[4],表示 maze[0][0] 與 maze[1][0] 相對位移量為 4,存取 maze[1][3] 時,表示存取位置相對於 maze[1][0] 位移3個單位。
<br>
若宣告 maze[3][5] 的話,記憶體位置的指定是如何呢?在這個陣列中 5 的意義是 maze[0][0]、maze[1][0] 與 maze[2][0] 間都是 5 個位移量,如下圖所示:

## 指標 Pointer
### & 取址運算子
&n 是n的位置,位置採16進位
### * 指標
```cpp=
資料型態* 變數名
int *n
```
n的型態為int*
此變數用來儲位置
```cpp=
int main() {
int n = 10;
int *p = &n ;
cout << "n 變數的位址:" << &n << endl
<< "p 儲存的位址:" << p << endl;
return 0;
}
output:
n 變數的位址:0x61feb8
p 儲存的位址:0x61feb8(不一定一樣)
```
### * 反參照運算子
與指標的符號一模一樣,可以使用提取運算子來拿指標儲存位置處的物件
```cpp=
int main() {
int n = 10;
int *p = &n;
cout << "指標 p 儲存的位址:" << p << endl
<< "提取 p 儲存位址處的物件:" << *p << endl;
return 0;
}
輸出:
指標 p 儲存的位址:0x61feb8(不一定一樣)
提取 p 儲存位址處的物件:10
```
### 指標與 const 型態
用 const 宣告的變數,必須使用對應的 const 型態指標才可以。
```cpp=
const int n = 10;
const int *p = &n;
```
要留意的是,const int *p 宣告的 p 並不是常數,p 可以儲存不同的位址。
```cpp=
#include <iostream>
using namespace std;
int main() {
const int n = 10;
const int m = 20;
const int *p = &n;
cout << p << endl;
p = &m;
cout << p << endl;
return 0;
}
輸出:
0x61feb8
0x61feb4(不一定一樣)
```
如果想令指標儲存的值無法變動,必須建立指標常數,先來看看來源變數沒有 const 的情況:
```cpp=
int n = 10;
int m = 20;
int* const p = &n;
p = &m; // error: assignment of read-only variable 'p'
```
如果 n、m 被 const 修飾,那麼就必須如下建立指標常數:
```cpp=
const int n = 10;
const int m = 20;
const int* const p = &n;
p = &m; // error: assignment of read-only variable 'p'
```
### 指標與函數
對於傳址進function是如何運作有疑問的人可以看看這個例子:
```cpp=
#include <bits/stdc++.h>
using namespace std;
void sw( int *a, int *b ) {
cout << "現在有一個變數在sw內,名字叫a,存的東西是:" << a << endl
<< "然後有一個變數在sw內,名字叫b,存的東西是:" << b << endl
<< endl;
int *x = a; //創了一個變數x,x存的東西要是一個位址,a現在存的東西是位址,所以可以給他
a = b; //把sw內,a存的位址改成b存的位址
b = x; //把sw內,b存的位址改成剛剛x存的位址
cout << "現在sw內,a裡面存的東西是:" << a << endl
<< "現在sw內,b裡面存的東西是:" << b << endl
<< endl;
cout << "實際上sw內a的記憶體位置是:" << &a << endl
<< "實際上sw內b的記憶體位置是:" << &b << endl
<< endl;
}
int main() {
int a = 10;
int b = 20;
cout << "現在在main裡面,經過sw前,a的位址是:" << &a << endl
<< "現在在main裡面,經過sw前,b的位址是:" << &b << endl
<< endl;
sw( &a, &b );
cout << "現在在main裡面,經過sw後,a的位址是:" << &a << endl
<< "現在在main裡面,經過sw後,b的位址是:" << &b << endl
<< endl;
system( "pause" );
return 0;
}
輸出:
現在在main裡面,經過sw前,a的位址是:0x61ff0c
現在在main裡面,經過sw前,b的位址是:0x61ff08
現在有一個變數在sw內,名字叫a,存的東西是:0x61ff0c
然後有一個變數在sw內,名字叫b,存的東西是:0x61ff08
現在sw內,a裡面存的東西是:0x61ff08
現在sw內,b裡面存的東西是:0x61ff0c
實際上sw內a的記憶體位置是:0x61fef0
實際上sw內b的記憶體位置是:0x61fef4
現在在main裡面,經過sw後,a的位址是:0x61ff0c
現在在main裡面,經過sw後,b的位址是:0x61ff08
```
可以看見在sw內仍會間接的產生一個變數來複製a與b的位置,但相較於值皆傳值進去,傳址的效率仍會快一些。
### 指標的運算
指標加法與減法與一般數值的加減不同,在指標運算上加 1 ,表示前進一個資料型態的記憶體長度,例如 int ,長度為 4 個位元組,在 int* 型態的指標上加 1,表示在位址上前進 4 個位元組的長度:
```cpp=
#include <iostream>
using namespace std;
int main() {
int *p = 0;
cout << "p 位址:" << p << endl
<< "p + 1:" << p + 1 << endl
<< "p + 2:" << p + 2 << endl;
return 0;
}
輸出:
p 位址:0
p + 1:0x4
p + 2:0x8
```
如果你不曉得某型態的記憶體空間時,可以使用sizeof()函式:
```cpp=
cout << sizeof(double);
輸出: 8
```
### 指標與陣列
https://openhome.cc/Gossip/CppGossip/PointerAndArray.html
### 指標的指標
指標的指標是拿來儲存記憶體位址的,差別在於指標的**型態**,可以看看底下的範例:
```cpp=
#include <iostream>
using namespace std;
int main() {
int n = 10;
int *p1 = &n;
int **p2 = &p1;
cout << "n 位址:" << p1 << endl
<< "p1 位址:" << p2 << endl;
return 0;
}
輸出: n 位址:0x61feb8
p1 位址:0x61feb4
```
> n 儲存了 10,n 的位址 0x61feb8,指定給 p1 儲存,而 p1 的位址是 0x61feb4,指定給 p2 儲存。
n的型態是int,因此 &n 的型態為`int*` ,所以宣告p1時宣告的型態為int*,而 &p1 的型態則為`int**`, 因此宣告p2時宣告的型態為int**。
指標的指標好用的地方在於運算多維陣列扮演的作用,因為多維陣列是由陣列的陣列所組成的,實際上就是數段一維陣列所構成的,而若對變數取址後遞增1,則會位移一整個陣列空間。
<br>
在宣告二維陣列時,我們需要宣告每段一維陣列的長度為何:
```cpp=
int arr[2][3] = {{10, 20, 30}, {40, 50, 60}};
int (*p)[3] = arr;
```
同樣的,在宣告三維陣列時,我們需要宣告每段二為陣列的長度為何:
```cpp=
int arr[2][2][3] = { {{1, 2, 3},{4, 5, 6}} , {{7, 8, 9},{10, 11, 12}} };
int (*p)[2][3] = arr;
```
在使用時可以直接使用auto比較方便:
```cpp=
int arr1[2][3] = {{10, 20, 30}, {40, 50, 60}};
auto p1 = arr1;
int arr2[1][2][3] = {{{10, 20, 30}, {40, 50, 60}}};
auto p2 = arr2;
```
再利用前面談二維陣列時的例子,如果我們要印出一個二維陣列,也可以利用指標的方式來操作,如下:
```cpp=
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(int (*it)[3] = begin(maze); it < end(maze); it++) {
int *row = *it;
for(int i = 0; i < 3; i++) {
cout << row[i] << "\t";
}
cout << endl;
}
return 0;
}
輸出:
1 2 3
4 5 6
```
### new and delete
## 物件別名 (Reference 參考)
https://blog.xuite.net/chuangyf0917/blog/13649687
https://www.geeksforgeeks.org/references-in-c/
Reference是物件的別名,你可以把他想成某個變數的綽號,對Reference進行的任何存取,都會對原物件進行操作。 在C++裡,物件不單只是指類別,而是指記憶體裡的一塊資料,換句話說,Reference類似pointer,但reference指到的是記憶體位址內的數值,pointer則是記憶體的位址。 另外,一個函式內不能有相同名稱的兩個Reference。
要定義Reference,是在型態關鍵字後加上&運算子,例如:
```cpp=
int n = 10; // 定義變數
int *p = &n; // 定義指標,儲存 n 的位址
int &r = n; // 定義參考,是 n 的別名
```
<br>
要注意的是,Reference後面必須要有指定的物件,否則無法通過編譯,如: `int &r;` 就會出錯。
對Reference的進行的任何存取,都會對原物件進行操作,如:
```cpp=
#include <iostream>
using namespace std;
int main() {
int n = 10;
int &r = n;
cout << "n:" << n << endl
<< "r:" << r << endl;
r = 20;
cout << "n:" << n << endl
<< "r:" << r << endl;
return 0;
}
輸出:
n:10
r:10
n:20
r:20
```
指標也可以使用Reference,例如:
```cpp=
int n = 10;
int *p = &n;
int *&r = p; // 也就是 int* &r = p, int* 是型態, r是指標p的Reference
```
陣列也可以,但需要指定長度,如:
```cpp=
int arr[] = {1, 2};
int (&r)[2] = arr;
```
<br>
既然陣列也可以使用,那麼再一次借用前面二維陣列的例子,我們也可以使用reference來幫助我們印出二維陣列,如下:
```cpp=
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(int (*it)[3] = begin(maze); it < end(maze); it++) {
int (&row)[3] = *it; // 使用reference
for(auto offset = begin(row); offset < end(row); offset++) {
int n = *offset;
cout << n << "\t";
}
cout << endl;
}
return 0;
}
```
不過這樣寫有點複雜,可以再搭配 for range 的語法與 auto 進行簡化,如下:
```cpp=
#include <iostream>
using namespace std;
int main() {
int maze[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
for(auto &row : maze) { // 使用reference
for(auto n : row) {
cout << n << "\t";
}
cout << endl;
}
return 0;
}
```
### Rvalue Reference
### Lvalue Reference
## 函式
### 語法
回傳值型態 函式名稱(參數1型態 參數名稱1, 參數2型態 參數名稱2, ... )
{ 執行內容;
return 回傳值; }
```cpp=
int pay( int hour )
{
if( hour <= 3 )
{
return hour*30;
}
else
{
return 3*30 + (hour-3)*20;
}
}
int main()
{
int n;
while( cin >> n )
{
cout << pay(n) << endl;
}
return 0;
}
```
函式一旦執行到return,就會立刻回傳,略過之後所有程式碼
### 引數(Argument) 與 參數(Parameter)
在呼叫函式時,提供給函式的資料稱為引數(argument),接受引數的稱為參數(parameter)。
例如以下的範例, n 是 parameter,型態為int,呼叫函式時提供 x 作為argument。
```cpp=
#include <iostream>
using namespace std;
int increment(int n) {
n = n + 1;
return n;
}
int main() {
int x = 10;
cout << increment(x) << endl;
cout << x << endl;
return 0;
}
輸出:
11
10
```
可以看見, n 雖然作了遞增運算,但是對 x 的儲存值沒有影響,x 最後仍是顯示 10,這是因為當我們呼叫function時,並不會真正改變我們傳進去的值。 在function中他會建一個新的變數(n),值與我們傳進的值(x)一樣。
若想真正改變到傳入的值,我們必須傳入變數的記憶體位址,直接對那塊記憶體位址的值進行操作,如下:
```cpp=
#include <iostream>
using namespace std;
int increment(int *n) {
*n = *n + 1;
return *n;
}
int main() {
int x = 10;
cout << increment(&x) << endl;
cout << x << endl;
return 0;
}
```
或者,我們可以傳入變數的Reference,如下:
```cpp=
#include <iostream>
using namespace std;
int increment(int &n) {
n = n + 1;
return n;
}
int main() {
int x = 10;
cout << increment(x) << endl;
cout << x << endl;
return 0;
}
輸出:
11
11
```
## Templete 樣板
```cpp=
template < 樣板參數型態 樣板參數名 [, 其他樣板參數] >
原型回傳型態 函數名(參數型態 原型參數名, ...) {
//prototype codes;
}
```
### Function Template (函數樣板)
```cpp=
//normal function:
int myAdd(int a, int b) {
return a + b;
}
//function template
template <class T> T myAdd(T a, T b)
{
return a + b;
}
```
### Class Template (類別樣板)
```cpp=
//normal class:
class myClass {
myClass& add(const myClass& a) {
return *this;
}
};
//class template:
template <class T>
class myTClass
{
T& add(const T& a) {
return *this;
}
};
```
<style>
.green {
color:#29E5A9;
}
.brown {
color:#990000;
}
.pink {
color:#DD9FBD;
}
.red {
color:#E71B18 ;
}
.blue {
color:#0b5394;
}
.purple {
color:#AC9FDD;
}
@-webkit-keyframes A
{
0% { color:#C10066;}
10% { color: #CC0000;}
20% { color: #E63F00; }
30% { color:#EE7700; }
40% { color: #DDAA00; }
50% { color:#EEEE00;}
60% { color: #99DD00;}
70% { color:#66DD00;}
80% { color: #00DDDD;}
90% { color: #0044BB;}
100% { color: #A500CC;}
}
#animation_title{
animation: A 3s ease 0s infinite alternate;
-webkit-animation: A 3s ease 0s infinite alternate;
}
</style>
<style>
a.redlink {
color:#DB1859;
}
a.redlink:link {
color:#DB1859;
text-decoration:none;
}
a.redlink:visiteid {
color:#DB1859;
text-decoration:none;
}
a.redlink:hover {
color:#19CABC;
text-decoration:none;
}
a.redlink:active {
color:#000000;
text-decoration:underline;
background:#FFFFFF;
}
</style>
<style type="text/css">
h1 {
font-size:;
color:#0b5394;
}
h2 {
font-size:;
color:#0b5394;
}
p {
font-size:;
color:;
}
h3 {
font-size: ;
color:#990000;
}
h4 {
font-size:;
color:#990000;
}
</style>