建構函式是 C++ 物件生命週期的起點,負責初始化資料成員、配置資源。本文系統整理六大類建構函式,搭配 Complex 複數類別範例,說明呼叫時機、參數形式與常見陷阱,並補充 C++11 移動建構與 explicit 關鍵字的現代用法。
當物件被建立時,編譯器自動呼叫建構函式完成:
class Counter {
public:
Counter() : m_value(0) {} // 初始化清單寫法(推薦)
private:
int m_value;
};
Counter c1; // m_value 自動為 0
| 類型 | 特徵 | 呼叫情境 | 備註 |
|---|---|---|---|
| 預設建構 | 無參數或全有預設值 | T obj; new T() | 若未自訂任何建構函式,編譯器會隱式產生空函式 |
| 一般建構(多載) | 參數列表不同 | T obj(arg); T obj{arg}; | 可定義多個,支援不同初始化方式 |
| 複製建構 | T(const T&) | 以現有物件初始化新物件 | 淺拷貝風險,含指標成員時必須深拷貝 |
| 移動建構 (C++11) | T(T&&) noexcept | 從右值(暫時物件)轉移資源 | 避免深拷貝開銷,標準庫容器大量依賴 |
| 轉換建構 | 單一非參考參數 | 隱式型別轉換 T obj = 3.14; | 加 explicit 禁止隱式轉換 |
| 委派建構 (C++11) | 在初始化清單呼叫同類別其他建構函式 | 減少重複程式碼 | T() : T(default_arg) {} |
Complex 完整實作示範#include <iostream>
using namespace std;
class Complex {
double m_real, m_imag;
public:
// 1. 預設建構
Complex() : m_real(0), m_imag(0) { cout << "Default ctor\n"; }
// 2. 一般建構
Complex(double r, double i) : m_real(r), m_imag(i) { cout << "General ctor\n"; }
// 3. 複製建構
Complex(const Complex& rhs) : m_real(rhs.m_real), m_imag(rhs.m_imag) { cout << "Copy ctor\n"; }
// 4. 移動建構
Complex(Complex&& rhs) noexcept : m_real(rhs.m_real), m_imag(rhs.m_imag) {
rhs.m_real = rhs.m_imag = 0;
cout << "Move ctor\n";
}
// 5. 轉換建構(加 explicit 防隱式轉換)
explicit Complex(double r) : m_real(r), m_imag(0) { cout << "Converting ctor\n"; }
// 6. 複製指派運算子
Complex& operator=(const Complex& rhs) {
if (this != &rhs) { m_real = rhs.m_real; m_imag = rhs.m_imag; }
cout << "Copy assign\n"; return *this;
}
// 7. 移動指派運算子
Complex& operator=(Complex&& rhs) noexcept {
if (this != &rhs) { m_real = rhs.m_real; m_imag = rhs.m_imag; rhs.m_real = rhs.m_imag = 0; }
cout << "Move assign\n"; return *this;
}
friend ostream& operator<<(ostream& os, const Complex& c) {
return os << '(' << c.m_real << ", " << c.m_imag << ')';
}
};
int main() {
Complex c1; // Default
Complex c2(1.0, 2.5); // General
Complex c3 = c2; // Copy ctor (複製初始化)
Complex c4; c4 = c2; // Copy assign (指派)
Complex c5 = Complex(3.0); // Converting ctor (需 explicit 則寫 Complex c5(3.0))
Complex c6 = std::move(c2); // Move ctor
c1 = std::move(c3); // Move assign
cout << c1 << c4 << c5 << c6 << endl;
}
Complex c3 = c2; → 複製初始化,呼叫複製建構函式Complex c4; c4 = c2; → 預設建構 + 複製指派若類別含原始指標 int* p,預設複製建構只複製指標值,導致雙重釋放。必須自訂:
Complex(const Complex& rhs) : m_real(rhs.m_real), m_imag(rhs.m_imag) {
p = new int(*rhs.p); // 深拷貝
}
explicit 的防禦性設計void func(Complex c);
func(3.14); // 若無 explicit,隱式轉換通過編譯,易造成誤用
func(Complex(3.14)); // 加上 explicit 後,必須顯式建構
explicit,除非確有隱式轉換需求noexcept,讓標準庫容器在重新配置記憶體時優先使用移動而非複製= default / = delete 明確表達意圖建構函式是 C++ 物件模型的基石,掌握其六種形式與資源管理規則,能寫出高效、安全的現代 C++ 程式碼。建議配合 C++ Core Guidelines 定期審視類別設計。