【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)