--- GA: G-RZYLL0RZGV --- ###### tags: `大一程設-下` `東華大學` `東華大學資管系` `基本程式概念` `資管經驗分享` Copy Constructor、Overloading Assignment Operator(下) === [TOC] ## 前言 針對 copy constructor 還有最後的一個重點要提,就是何時會觸發 copy constructor 呢? 在撰寫程式的時候請一定要注意,否則你就會看著結果然後不知道錯在哪裏。 ## Calling a Copy Constructor 在甚麼樣的情況下會呼叫 copy constructor 呢? * 用同類別的物件來實體化同類別物件的時候 * `Animal a2(a1)` or `Animal a2 = a1` * 前兩篇筆記再跟大家談的 * 當你的 function 的 return type 是類別的時候 * 當你把類別物件作為 call-by-value 參數丟進 function 的時候 ### 當 function 的 return type 是類別 假設我們有一 function 如下,不隸屬於類別,在類別之外。當然也可以定義 function 在類別內,甚至設計為 friend,但這邊就用最簡單的例子解釋。 ```cpp= Animal create_animal(Animal& a1, Animal& a2){ Animal a3; a3.set_age(a1.get_age()+a2.get_age()); return a3; } int main(){ Animal a1(20); Animal a2 = a1; // call copy constructor Animal a3 = create_animal(a1, a2); // call copy constructor } ``` 上面的第九行,因為 create_animal 的 return type 是 Animal,所以自然形成了跟第八行一樣的局面,因此也呼叫了 copy constructor。 ### 延伸思考(Optional,不想看可略過) 延伸思考,如果今天寫成這樣,會發生甚麼事呢? 還能產生如你所想的結果嗎? 這邊就給大家自己思考,其實都換湯不換藥,如果錯了一定有原因,而且我都教過了哦,請看清楚參數們! 如果錯了可以怎麼改呢? > 程式碼會說話! > [name=Orange] ```cpp= class Animal(){ //上面其餘皆同 void operator = (Animal& right_side){ this->age = new int; *(this->age) = *(right_side.age); } }; Animal create_animal(Animal& a1, Animal& a2){ Animal a3; a3.set_age(a1.get_age()+a2.get_age()); return a3; } int main(){ Animal a1(20); Animal a2 = a1; Animal a3; a3 = create_animal(a1, a2); //以下省略 } ``` ### 當類別物件作為 call-by-value 參數 舉例我們有如下的一個 function,不隸屬於類別,在類別之外。當然也可以定義 function 在類別內,甚至設計為 friend,但這邊就用最簡單的例子解釋。 ```cpp= // class 省略 int add_animal_age(Animal a, Animal b){ return a.get_age() + b.get_age(); } int main(){ Animal a1(20); Animal a2(30); cout << add_animal_age(a1, a2); } ``` 如果你沒有忘記 call-by-value,呼叫這個 function 的時候,其實發生了下面這件事。 `Animal a = a1`、`Animal b = a2` -> 請記得此處觸發 copy constructor 把 main 內的 a1、a2 作為參數丟進 function,而因為 call-by-value 單純複製值,所以如果你的 copy constructor 沒寫好就完蛋了。 下面貼上完整例子,可以稍微看一下印出 `copy constructor` 是在何時,以及 Destructor 最後的回收順序。 ```cpp= class Animal{ public: Animal(){ this->age = NULL; } void set_age(int a){ this->age = new int; *(this->age) = a; } int get_age(){ cout << "address of age: " << &(this->age) << ", "; cout << "value of age: " << this->age << endl; return *(this->age); } Animal(int a){ this->age = new int; *(this->age) = a; } Animal(const Animal& a){ cout << "copy constructor" << endl; this->age = new int; *(this->age)= *(a.age); } ~Animal(){ cout << "-------------" << endl; cout << "Destructor:" << endl; cout << &(this->age) << endl; cout << this->age << endl; cout << *(this->age) << endl; cout << "-----------" << endl; delete this->age; } void operator = (Animal& right_side){ this->age = new int; *(this->age) = *(right_side.age); } private: int* age; }; int add_animal_age(Animal a1, Animal a2){ return a1.get_age() + a2.get_age(); } int main(int argc, char** argv) { Animal a1(20); Animal a2 = a1; cout << "A1's age: " << a1.get_age() << endl; cout << "A2's age: " << a2.get_age() << endl; cout << "-------------" << endl; a1.set_age(25); cout << "A1's age: " << a1.get_age() << endl; cout << "A2's age: " << a2.get_age() << endl; cout << "-------------" << endl; cout << add_animal_age(a1,a2) << endl; return 0; } ``` 輸出如下 : ![](https://i.imgur.com/DD21FZ9.png) 第一行的 copy constructor,無庸置疑是上面程式碼第 47 行印出來的。 第二個和第三個 copy constructor,則是在呼叫 `add_animal_age` 把 main 內的兩個 Animal 當作參數丟進 function 時觸發的。 然後因為我們 copy constructor 有寫好,所以沒有發生操弄同一塊記憶體的情況。 接著是 Destructor 解構順序,可以看到是先 function 的 a1,function 的 a2,main 內的 a2,main 內的 a1。 為何 function 的 a1、a2 先於 main 的釋放? 因為 function 週期一結束,釋放所需記憶體,call-by-value 的緣故記憶體直接釋放。 > 希望過往教過的概念不要都忘記摟,全部都環環相扣在一起哦。 > [name=Orange] ## 總結 相信看了三篇,如果你挺過來看到了這裡,可以理解為甚麼我會說這是一下最麻煩的章節,copy constructor 想表達的概念其實很簡單,但程式碼就是煩人了點,但我相信如果過往你是認真學、認真理解的話,我相信絕對難不倒你,因為根本就是把舊有概念全部合在一起而已,相信認真學習的你會更上層樓! 而且他想表達的概念在記憶體操作上是真的非常非常重要的哦!