一、概念
????????構造函數(Constructor) 是一種特殊的成員函數,用于在創建對象時初始化對象的狀態(即成員變量)。它的主要作用是保證對象在創建時具有有效的初始值。
二、特點
與類同名:
- 構造函數的名稱與類名相同,沒有返回值(甚至不能寫
void
)。
自動調用:
- 構造函數在對象創建時自動調用,無需顯式調用。
可以重載:
- 構造函數可以有多個版本,依賴于參數個數和類型(重載)。
無返回值:
- 構造函數沒有返回值,也不能通過返回值來傳遞信息。
默認構造函數:
- 如果用戶沒有定義構造函數,編譯器會自動生成一個無參的默認構造函數。
- 如果用戶定義了任何構造函數,編譯器將不再生成默認構造函數。
三、種類
1. 默認構造函數
無參數的構造函數,稱為默認構造函數。
- 作用:初始化對象為默認狀態。
- 示例:
class Example {
public:Example() { // 默認構造函數cout << "Default constructor called" << endl;}
};int main() {Example obj; // 自動調用默認構造函數return 0;
}
2. 帶參數的構造函數
構造函數可以帶參數,用于初始化對象時傳遞參數。
- 示例:
class Example {
private:int value;
public:Example(int v) { // 帶參數的構造函數value = v;cout << "Constructor called with value: " << value << endl;}
};int main() {Example obj(10); // 調用帶參數的構造函數return 0;
}
3. 拷貝構造函數
拷貝構造函數用于創建一個對象時,以另一個同類型的對象對其初始化。
- 特點:參數是同類的引用類型,通常形式為
ClassName(const ClassName &obj)
。 - 示例:
class Example {
private:int value;
public:Example(int v) {value = v;cout << "Parameterized constructor called" << endl;}Example(const Example &obj) { // 拷貝構造函數value = obj.value;cout << "Copy constructor called" << endl;}
};int main() {Example obj1(10); // 調用帶參數的構造函數Example obj2 = obj1; // 調用拷貝構造函數return 0;
}
4. 委托構造函數
一個構造函數可以調用另一個構造函數來簡化代碼邏輯。
- 示例:
class Example {
private:int value;
public:Example() : Example(0) { // 調用另一個構造函數cout << "Default constructor called" << endl;}Example(int v) {value = v;cout << "Parameterized constructor called with value: " << value << endl;}
};int main() {Example obj; // 調用默認構造函數,同時委托到帶參數的構造函數return 0;
}
5. 默認和刪除的構造函數
- 可以顯式指定構造函數為默認的或刪除的(C++11 引入)。
- 示例:
class Example {
public:Example() = default; // 默認構造函數Example(int) = delete; // 禁止使用此構造函數
};int main() {Example obj1; // 可以// Example obj2(10); // 錯誤,無法使用刪除的構造函數return 0;
}
四、構造函數的使用與初始化列表
初始化成員變量
可以在構造函數中直接初始化成員變量,或使用 初始化列表 初始化。
- 示例:
class Example {
private:int a;int b;
public:Example(int x, int y) : a(x), b(y) { // 初始化列表cout << "a = " << a << ", b = " << b << endl;}
};int main() {Example obj(10, 20); // 調用構造函數return 0;
}
初始化列表
- 初始化列表在對象構造時直接賦值,而不是先默認構造后再賦值,因此效率更高。
- 某些情況必須使用初始化列表,例如常量成員變量或引用類型。
五、子類構造函數
工作流程
- 創建子類對象時,必須先調用父類的構造函數。
- 如果子類的構造函數沒有顯式調用父類的構造函數,則會默認調用父類的無參構造函數。
- 如果父類沒有無參構造函數,子類必須顯式調用父類的某個構造函數。
示例:
#include <iostream>
using namespace std;class Parent {
public:Parent(int x) {cout << "Parent constructor called with value: " << x << endl;}
};class Child : public Parent {
public:Child(int x) : Parent(x) { // 顯式調用父類的構造函數cout << "Child constructor called" << endl;}
};int main() {Child obj(10); // 調用子類構造函數return 0;
}
輸出:
Parent constructor called with value: 10
Child constructor called
六、構造函數的注意點
沒有返回值: 構造函數沒有返回值,不能使用 return
返回值。
拷貝構造函數的深拷貝與淺拷貝: 如果類中有指針類型成員變量,拷貝構造函數默認執行淺拷貝,這可能會導致資源沖突,建議實現深拷貝。
靜態成員的初始化: 靜態成員變量需要在類外初始化,不能通過構造函數初始化。
繼承中的構造函數:父類的構造函數不會被子類繼承,但可以通過子類構造函數顯式調用。
構造函數與析構函數的關系
- 析構函數與構造函數相反,用于對象銷毀時釋放資源。
- 析構函數在子類對象銷毀時按照從子類到父類的順序調用。
示例:
#include <iostream>
using namespace std;class Parent {
public:Parent() { cout << "Parent constructor called" << endl; }~Parent() { cout << "Parent destructor called" << endl; }
};class Child : public Parent {
public:Child() { cout << "Child constructor called" << endl; }~Child() { cout << "Child destructor called" << endl; }
};int main() {Child obj; // 創建子類對象return 0;
}
輸出:
Parent constructor called
Child constructor called
Child destructor called
Parent destructor called
七、總結
- 構造函數是初始化對象的入口,可以重載。
- 帶參數構造函數和拷貝構造函數提供了靈活的初始化方式。
- 初始化列表是高效的初始化方式,尤其適合常量或引用類型。
- 父類的構造函數必須在子類構造函數中顯式調用(如果沒有無參構造函數)。
- 析構函數與構造函數互補,用于釋放資源。