---
GA: G-RZYLL0RZGV
---
###### tags: `大一程設-下` `東華大學` `東華大學資管系` `基本程式概念` `資管經驗分享`
建構子 constructor & 認識 this
===
[TOC]
## 前言
還記得類別需要有非常多的屬性跟方法來表達他的藍圖,比方說一個叫做 `person` 的類別,底下可能有 `name、age、salary、birth` 之類的屬性,每一個人類都會有這些屬性去詮釋他。
那今天要真的建立出一個人類把他拿來用,我們會說要實體化一個 person 物件,人一誕生的時候,有些屬性也會伴隨著一起誕生,比方說一個小 baby 出生馬上就有生日,這是他天生的屬性。一個人會有 4 隻腳,這也是天生的屬性。
> 而建構子的概念,就是在表達「天生」這件事情。
## 建構子 constructor
今天一個類別都會有一個預設的建構子,如果我們不寫他,程式也會自己執行他,因為是預設的。<span style="color:red">**預設的建構子與類別同名,沒有參數,是一個方法(函式)**</span>
<span style="color:red">**自己建立的建構子可以有無限個參數,視自己需求而定。**</span>
> 相信大家都有看到 `this->`,會放在之後教大家。
> 大家可以先記 `this` 代表類別本身,`->` 也是代表 `的` 的意思。
> [name=Orange]
>
```cpp=
class person{
public:
// default constructor 預設的建構子
person(){
this->name = "";
this->birth = "";
}
// 我們自己建立的建構子
person(string n, string b){
this->name = n;
this->birth = b;
}
// getter
string get_name(){
return this->name;
}
string get_birth(){
return this->birth;
}
// setter
void set_name(string n){
this->name = n;
}
void set_birth(string b){
this->birth = b;
}
private:
string name;
string birth;
};
int main(){
person p1; // p1 物件呼叫的是預設的建構子,不用加括號
p1.set_name("john");
p1.set_birth("0101");
cout << p1.get_name() << " " << p1.get_birth() << endl;
person p2("orange", "0808"); // p2 物件呼叫的是有兩個參數的建構子
cout << p2.get_name() << " " << p2.get_birth() << endl;
return 0;
}
```
> 經過測試,現在預設的建構子必須要寫哦,如果沒有寫卻試圖實體化物件會發生下圖的錯誤。
> 若直接呼叫自定義建構子是沒問題的,但都還是養成都要寫的習慣。
> 
> 因此可以得知,現在必須要撰寫 default constructor。
程式碼說明:
* 針對 person 物件 p1
* 上面這個範例,我們在 main 實體化了兩個 person,一個叫做 p1,其被實體化時沒有傳參數,所以會呼叫預設建構子,預設的建構子我們自己設計,會把名字跟出生月日設成空字串。
* 而我們設計 getter & setter 來為物件設值,所以你可以發現 p1 物件都是在 main 內呼叫方法來設值。
* 所以可以想像成是「<span style="color:red">**後天的**</span>」賦予它狀態。
* 針對 person 物件 p2
* p2 物件在物件一被實體化的時候即傳入兩個參數,會呼叫我們自己建立的建構子。
* 所以可以發現在 main 內不用透過呼叫 setter 去附值,對於 p2 而言,他的名字與生日是「<span style="color:red">**天生的**</span>」狀態。
> 簡單的說,只要物件一被實體化,依靠建構子建立好的屬性,我們就會說是他天生的狀態。
> [name=Orange]
## 認識 this
在 C++ 裡面呢,對於類別而言有一個很特別的東西叫做 this,他是一種指標變數,而且**只能作用在類別**裡面,他**不屬於物件本身**,當今天任何一個物件被實體化的時候,this 這個指標變數就會指向被實體化的那個物件,簡單的說,每個物件都會有一個 this 指標。
還記得我說過 class/structure 就像規格書,表達一個物件要被實體化時需要遵守的規定,謹記 this 只會被寫在類別中。
你可以看看下面這個例子,會發現對於每個物件而言,this 會指向物件所在的記憶體位址。
```cpp=
class Person{
public:
Person(){
cout << "this pointer from class: " << this << endl;
this->name = "";
}
private:
string name;
};
int main(){
Person p1, p2;
cout << "p1 address from main: " << &p1 << endl
<< "p2 address from main: " << &p2 << endl;
return 0;
}
```

### ->,的
相信這個符號也是卡住大家,`.`、`::`、`->` 這三種符號對我來說都念做「的」,只是撰寫的情況不同,所以上面第五行就是,**被實體化物件「的」名字**。
而為什麼不寫 `.` 而要寫 `->` 呢?因為在 C++ 內,**只要你要去取用屬性或方法的人是由指標變數驅動的時候,就需要使用 `->`**,所以往後你看到的很多例子都會出現 `->` 這個符號。
但你可能會說,既然他是指標變數,那 `this->name` 不就像是在取值嗎,那其實不就是 `(*this).name` 嗎?
> 沒錯,就是這樣!
> [name=Orange]
但通常在 C++ 大家都會寫 `->`,因為比較方便,畢竟看到星號就是覺得麻煩。所以你可以試著把上面的 `this->` 修改成 `*this`,你會看到同樣的效果。
> this 真的很重要哦,C++ 會有,javascript、java 等超多語言都有 this 哦。
### 避免類別屬性與參數同名
而用 this 還有一個很好的區隔作用,就是發生類別的屬性跟參數同名的時候。
```cpp=
class Person{
public:
Person(){ this->name = ""; }
Person(string name){
this->name = name;
}
private:
string name;
};
```
你可以看看上面的例子,第四行的自定義建構子,參數與類別的屬性同名,為了區隔出一個是物件的屬性,一個是參數,所以我們需要寫 this,但如果今天沒有撞名的問題,那不寫 this 也是沒關係的,像下面這樣。
```cpp=
class Person{
public:
Person(){ name = ""; }
Person(string n){
name = n;
}
private:
string name;
};
```
我想應該非常好理解,如果撞名且不寫 this 的話會發生 `name = name`,程式根本無發分辨哪個 name 是誰。
其實 this 要注意的內容還有一些,上面這樣其實只佔了一半,但因為還沒有教到靜態方法,所以這邊就先不提,上面講的要會就好。
至於你說背後 this 怎麼運作的,可以去看下方 Reference 第三篇。
## Reference
* [this 指標](https://openhome.cc/Gossip/CppGossip/thisPointer.html)
* [C语言中“.”与“->”有什么区别?](https://farseerfc.me/dot-and-arrow-in-c.html)
* [C++ this指標的詳解 C++中this指標的用法詳解](https://www.796t.com/content/1543115291.html)