【C++ 筆記】建構子(Constructors) - part 18 === 目錄(Table of Contents): [TOC] --- 很感謝你點進來這篇文章。 你好,我並不是什麼 C++、程式語言的專家,所以本文若有些錯誤麻煩請各位鞭大力一點,我極需各位的指正及指導!!本系列文章的性質主要以詼諧的口吻,一派輕鬆的態度自學程式語言,如果你喜歡,麻煩留言說聲文章讚讚吧! 建構子(Constructors) --- 建構子(Constructors),又稱建構函數、建構式,為一種特別的 class members,每次實例化該類別的物件時,編譯器都會呼叫建構子(表示每次建立物件都會用到此函數)。***建構子與 class 具有相同的名稱***,並且可以在 class 中進行內部定義或外部定義。 另外建構子也不會回傳任何型態,也不會回傳 void。 :::success 可以想像建構子就是做初始化的動作。 ::: 在 C++ 中,共有下列四種建構子: * 預設建構子(Default Constructors) * 參數化建構子(Parameterized Constructors) * 複製建構子(Copy Constructors) * 移動建構子(Move Constructors) -> 此篇不談 以下是一個練習範例(展示預設建構子),~~新手適用的建構子教學~~: ```cpp= #include <iostream> #include <string> using namespace std; // 定義 class 叫 Person class Person { private: string name; int age; public: // 預設建構子(Default Constructors) Person() { name = "Unknown"; age = 0; cout << "預設建構子被呼叫了" << endl; } // 內建成員函數(方法):顯示成員資訊 void displayInfo() const { cout << "姓名: " << name << ", 年齡: " << age << endl; } }; int main() { // 建立物件時會自動呼叫預設建構子 Person p1; // 呼叫方法顯示資訊 p1.displayInfo(); return 0; } ``` 輸出結果: ``` 預設建構子被呼叫了 姓名: Unknown, 年齡: 0 ``` --- ### 參數化建構子(Parameterized Constructors) --- 就是帶有參數的建構子,因為他本身還是個函數嘛,以下是個範例: ```cpp= #include <iostream> #include <string> using namespace std; // 定義 class Car class Car { private: string brand; string model; int year; public: // 參數化建構子 Car(string b, string m, int y) { brand = b; model = m; year = y; } // 方法:顯示車輛資訊 void displayInfo() { cout << "品牌: " << brand << ", 車型: " << model << ", 出廠年份: " << year << endl; } }; int main() { // 用參數化建構子建立物件 Car car1("Toyota", "Corolla", 2020); Car car2("Tesla", "Model 3", 2022); car1.displayInfo(); car2.displayInfo(); return 0; } ``` 輸出結果: ``` 品牌: Toyota, 車型: Corolla, 出廠年份: 2020 品牌: Tesla, 車型: Model 3, 出廠年份: 2022 ``` --- ### 複製建構子(Copy Constructors) --- 複製建構子(Copy Constructor)是一種特殊的建構子,用來建立一個新物件,並以同型態的另一個已存在的物件來初始化它。 格式: ```cpp= classname (const classname &obj) { // Body of Copy Constructor } ``` `ClassName`:類別名稱。 `const ClassName& obj`:以參考方式傳遞的另一個物件,通常使用 const 使源物件不被修改。 const 看你要不要加,但加它是為了讓程式設計師不會錯誤地修改 `obj`。 這個 obj 就是 object,物件的意思。 複製建構子用於以下三種情形: * 透過使用另一個同型態的物件來初始化新建立的物件。 * 複製物件把它當作參數傳遞給函數。 * 複製對象,並從函數回傳這個對象。 若無明確定義複製建構子,編譯器會自動產生,該建構子會執行淺層複製(Shallow Copy): * 將 class 中的每個成員的值逐一複製。 * 若 class 中包含指標成員,預設複製建構子會直接複製指標的地址,導致兩物件共享記憶體區域。 簡言之,淺複製只會複製指針,而不會複製他實際上的"源頭",若刪除原本的物件,就可能導致空指針(NULL Pointer)。 與淺複製相反的,就是深層複製(Deep Copy),要使用深層複製,就是直接將 Copy Constructor 明確定義出來。 另外 Deep Copy 會為新物件分配獨立的記憶體空間,並複製資料內容與指標。 以下是一個範例: ```cpp= #include <iostream> using namespace std; // 建立一個測試 class // Create a demo class class A { public: int x; }; int main() { // 建立一個物件 a1 // Creating an a1 object A a1; a1.x = 10; cout << "a1's x = " << a1.x << endl; // 用 a1 建立另一個物件 // Creating another object using a1 // 正在呼叫複製建構子 // Copy Constructor Calling A a2(a1); cout << "a2's x = " << a2.x; return 0; } ``` Source:https://www.geeksforgeeks.org/copy-constructor-in-cpp/ 輸出結果: ``` a1's x = 10 a2's x = 10 ``` 透過上述範例,可發現我們並未定義「建構子」,但卻能夠使用同一個類別現有的物件來建立一個物件。那此時 C++ 編譯器就會建立一個簡單的複製建構子,也稱隱式複製建構子(implicit copy constructor)。 以下是一個範例,當我們定義 Copy Constructor: ```cpp= #include <iostream> using namespace std; // 建立一個測試 class // Create a demo class class A { public: int x; A(){}; // 定義一個 Copy Constructor // Copy Constructor definition A (A& t) { x = t.x; cout << "Calling copy constructor" << endl; } }; int main() { // 建立 a1 物件 // Creating an a1 object A a1; a1.x = 10; cout << "a1's x = " << a1.x << endl; // 用 a1 建立另一個物件 // Creating another object using a1 // 正在呼叫 Copy Constructor // Copy Constructor Calling A a2(a1); cout << "a2's x = " << a2.x; return 0; } ``` Source:https://www.geeksforgeeks.org/copy-constructor-in-cpp/ 輸出結果: ``` a1's x = 10 Calling copy constructor a2's x = 10 ``` 以上程式碼第十行,`A(){};` 為定義一個預設建構子,但他是空的。那他有什麼屁用呢? 若刪掉此行,則會因為 class 中已定義了一個複製建構子 `A(A& t)`,編譯器將不再自動產生預設建構子。所以,`A a1;` 物件會無法建立,編譯器直接報錯。 這是先後順序問題,你要先建立物件才能用 Copy Constructor,因此需要預設建構子為基礎。 以下是個範例,有關於深層複製(Deep Copy): ```cpp= #include <cstring> #include <iostream> using namespace std; class String { private: char* s; int size; public: String(const char* str = NULL); ~String() { delete[] s; } // 複製建構子 // Copy constructor String(const String&); void print() { cout << s << endl; } void change(const char*); }; // 成員函數及建構子的定義 // Definitions of constructor and memeber functions String::String(const char* str) { size = strlen(str); s = new char[size + 1]; strcpy(s, str); } String::String(const String& old_str) { size = old_str.size; s = new char[size + 1]; strcpy(s, old_str.s); } void String::change(const char* str) { delete[] s; size = strlen(str); s = new char[size + 1]; strcpy(s, str); } int main() { String str1("GeeksQuiz"); // 從 str1 建立 str2 // Create str2 from str1 String str2 = str1; str1.print(); // what is printed ? 印出了啥 ? str2.print(); str2.change("GeeksforGeeks"); // 注意此行只有 change str2 str1.print(); // what is printed now ? 現在他印出了啥 ? str2.print(); return 0; } ``` Source:https://www.geeksforgeeks.org/copy-constructor-in-cpp/ 輸出結果: ``` GeeksQuiz GeeksQuiz GeeksQuiz GeeksforGeeks ``` 此範例使用到了 new、delete 運算子,這兩個運算子的意義分別是「自行對記憶體配置」、「自行刪除掉記憶體」。 有關 new、delete 運算子的使用上,是要非常小心的,若一直用 new 一直給記憶體資源,而不用 delete 刪除的話,那麼記憶體會吃滿。 詳情可至此網站了解:https://openhome.cc/Gossip/CppGossip/newDelete.html 第十二行中,用到了解構子的語法:`~String() { delete[] s; }`。 :::success 解構子(Destructor),語法就是用一個波浪符號 `~` 後面加上類別名稱,與建構子宣告方式類似。它不會回傳任何值,也不能帶有任何參數。解構子有助於在跳出程式(例如關閉檔案、釋放記憶體等)前釋放資源。 ::: 那第十二行就是在說當離開作用域或程式結束時,會自動刪除掉 s 這個成員。 總結 --- 建構子(Constructors) 是 C++ 類別中特別的成員函數,每次建立物件時都會自動被呼叫,用於初始化物件。 :::success 建構子與類別名稱相同,沒有回傳值。 -> 非常重要 ::: **C++ 提供了四種建構子:** 1. 預設建構子(Default Constructor):不帶參數,將物件設定預設值。 2. 參數化建構子(Parameterized Constructor):允許在建立物件時傳入參數進行初始化。 3. 複製建構子(Copy Constructor):用於建立新物件,並以現有的同型態物件初始化它。若未定義,編譯器會使用淺層複製(Shallow Copy),但當類別包含動態記憶體分配時,應使用深層複製(Deep Copy)來避免記憶體錯誤。 4. 移動建構子(Move Constructor)->本篇不談 **Copy Constructor Application:** * 以現有物件初始化新物件。-> 如:`myClass a2 = a1` * 當物件作為參數傳遞給函數時。 * 當函數回傳物件時。 若類別包含指標成員,應用深層複製確保每物件有獨立記憶體區域,避免記憶體共享導致的錯誤。 參考資料 --- [new 與 delete](https://openhome.cc/Gossip/CppGossip/newDelete.html) [Copy Constructor in C++ - GeeksforGeeks](https://www.geeksforgeeks.org/copy-constructor-in-cpp/) [C++ Classes and Objects - GeeksforGeeks](https://www.geeksforgeeks.org/c-classes-and-objects/) [C++ Constructors | W3Schools](https://www.w3schools.com/cpp/cpp_constructors.asp) [C++ 类构造函数 & 析构函数 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-constructor-destructor.html) [C++ 拷贝构造函数 | 菜鸟教程](https://www.runoob.com/cplusplus/cpp-copy-constructor.html)