--- 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; } ``` > 經過測試,現在預設的建構子必須要寫哦,如果沒有寫卻試圖實體化物件會發生下圖的錯誤。 > 若直接呼叫自定義建構子是沒問題的,但都還是養成都要寫的習慣。 > ![](https://i.imgur.com/o4gjJoY.png) > 因此可以得知,現在必須要撰寫 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; } ``` ![](https://i.imgur.com/2ngmXVT.png) ### ->,的 相信這個符號也是卡住大家,`.`、`::`、`->` 這三種符號對我來說都念做「的」,只是撰寫的情況不同,所以上面第五行就是,**被實體化物件「的」名字**。 而為什麼不寫 `.` 而要寫 `->` 呢?因為在 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)