Try  HackMD Logo HackMD
tags: 大一程設-下 東華大學 東華大學資管系 基本程式概念 資管經驗分享

建構子 constructor & 認識 this

前言

還記得類別需要有非常多的屬性跟方法來表達他的藍圖,比方說一個叫做 person 的類別,底下可能有 name、age、salary、birth 之類的屬性,每一個人類都會有這些屬性去詮釋他。

那今天要真的建立出一個人類把他拿來用,我們會說要實體化一個 person 物件,人一誕生的時候,有些屬性也會伴隨著一起誕生,比方說一個小 baby 出生馬上就有生日,這是他天生的屬性。一個人會有 4 隻腳,這也是天生的屬性。

而建構子的概念,就是在表達「天生」這件事情。

建構子 constructor

今天一個類別都會有一個預設的建構子,如果我們不寫他,程式也會自己執行他,因為是預設的。預設的建構子與類別同名,沒有參數,是一個方法(函式)

自己建立的建構子可以有無限個參數,視自己需求而定。

相信大家都有看到 this->,會放在之後教大家。
大家可以先記 this 代表類別本身,-> 也是代表 的意思。
Orange

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; }

經過測試,現在預設的建構子必須要寫哦,如果沒有寫卻試圖實體化物件會發生下圖的錯誤。
若直接呼叫自定義建構子是沒問題的,但都還是養成都要寫的習慣。

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

因此可以得知,現在必須要撰寫 default constructor。

程式碼說明:

  • 針對 person 物件 p1
    • 上面這個範例,我們在 main 實體化了兩個 person,一個叫做 p1,其被實體化時沒有傳參數,所以會呼叫預設建構子,預設的建構子我們自己設計,會把名字跟出生月日設成空字串。
    • 而我們設計 getter & setter 來為物件設值,所以你可以發現 p1 物件都是在 main 內呼叫方法來設值。
    • 所以可以想像成是「後天的」賦予它狀態。
  • 針對 person 物件 p2
    • p2 物件在物件一被實體化的時候即傳入兩個參數,會呼叫我們自己建立的建構子。
    • 所以可以發現在 main 內不用透過呼叫 setter 去附值,對於 p2 而言,他的名字與生日是「天生的」狀態。

簡單的說,只要物件一被實體化,依靠建構子建立好的屬性,我們就會說是他天生的狀態。
Orange

認識 this

在 C++ 裡面呢,對於類別而言有一個很特別的東西叫做 this,他是一種指標變數,而且只能作用在類別裡面,他不屬於物件本身,當今天任何一個物件被實體化的時候,this 這個指標變數就會指向被實體化的那個物件,簡單的說,每個物件都會有一個 this 指標。

還記得我說過 class/structure 就像規格書,表達一個物件要被實體化時需要遵守的規定,謹記 this 只會被寫在類別中。

你可以看看下面這個例子,會發現對於每個物件而言,this 會指向物件所在的記憶體位址。

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; }

Image Not Showing Possible Reasons
  • The image file may be corrupted
  • The server hosting the image is unavailable
  • The image path is incorrect
  • The image format is not supported
Learn More →

->,的

相信這個符號也是卡住大家,.::-> 這三種符號對我來說都念做「的」,只是撰寫的情況不同,所以上面第五行就是,被實體化物件「的」名字

而為什麼不寫 . 而要寫 -> 呢?因為在 C++ 內,只要你要去取用屬性或方法的人是由指標變數驅動的時候,就需要使用 ->,所以往後你看到的很多例子都會出現 -> 這個符號。

但你可能會說,既然他是指標變數,那 this->name 不就像是在取值嗎,那其實不就是 (*this).name 嗎?

沒錯,就是這樣!
Orange

但通常在 C++ 大家都會寫 ->,因為比較方便,畢竟看到星號就是覺得麻煩。所以你可以試著把上面的 this-> 修改成 *this,你會看到同樣的效果。

this 真的很重要哦,C++ 會有,javascript、java 等超多語言都有 this 哦。

避免類別屬性與參數同名

而用 this 還有一個很好的區隔作用,就是發生類別的屬性跟參數同名的時候。

class Person{ public: Person(){ this->name = ""; } Person(string name){ this->name = name; } private: string name; };

你可以看看上面的例子,第四行的自定義建構子,參數與類別的屬性同名,為了區隔出一個是物件的屬性,一個是參數,所以我們需要寫 this,但如果今天沒有撞名的問題,那不寫 this 也是沒關係的,像下面這樣。

class Person{ public: Person(){ name = ""; } Person(string n){ name = n; } private: string name; };

我想應該非常好理解,如果撞名且不寫 this 的話會發生 name = name,程式根本無發分辨哪個 name 是誰。

其實 this 要注意的內容還有一些,上面這樣其實只佔了一半,但因為還沒有教到靜態方法,所以這邊就先不提,上面講的要會就好。

至於你說背後 this 怎麼運作的,可以去看下方 Reference 第三篇。

Reference