首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> 關於C++

C++ 類的六大默認成員函數深度解析(上):構造、析構、拷貝、賦值

     編輯:關於C++

C++ 類的六大默認成員函數深度解析(上)

C++ 編譯器在類未顯式聲明時會隱式生成六個成員函數:預設構造、析構、拷貝構造、拷貝賦值、取址運算符(const/非 const)。本文重點剖析前四者的觸發時機、預設行為、淺拷貝陷阱與深拷貝正確實現模式。

一、構造函數

特徵

  • 同類名、無返回值、可重載、可帶參數、可定義在類內/外。
  • 無 this 指針(對象尚未構造完成)。
  • 若用戶聲明任一構造函數,編譯器不再生成預設構造函數。
  • 全缺省構造(所有參數有默認值)與無參構造二擇一,否則調用歧義。

初始化列表 vs 函數體賦值

// 低效:默認構造 + 賦值(兩步)
Date(int y, int m, int d) { year = y; month = m; day = d; }

// 高效:初始化列表(一步到位)
Date(int y, int m, int d) : year(y), month(m), day(d) {}
  • 必須用初始化列表的場景:const 成員、引用成員、無預設構造函數的類類型成員。
  • 類類型成員若不在列表初始化,會先調用其預設構造,再在函數體賦值——雙重開銷。

二、析構函數

  • 名稱 ~ClassName(),無參、無返回值、有 this、不可重載、一個類只能有一個。
  • 對象生命週期結束自動調用;後構造先析構(棧展開順序)。
  • 函數體不負責釋放對象自身記憶體,僅做資源清理(delete 持有的指針、關閉句柄等)。
  • 若類含裸指針管理動態資源,必須顯式聲明析構函數,否則預設析構不釋放資源 → 記憶體洩漏。

三、拷貝構造函數

簽名與調用時機

ClassName(const ClassName&); // 必須為 const 引用,否則按值傳遞導致無限遞歸
  • 用現有對象初始化新對象:T t2(t1); / T t2 = t1;
  • 值傳遞參數給函數
  • 函數值返回對象(NRVO/RVO 可優化掉)

預設行為:淺拷貝

逐位複製所有非靜態成員。對於裸指針成員,導致兩個對象指向同一塊堆內存 → 雙重 delete 崩潰。

class Person {
    char* name;
public:
    Person(const char* n) { name = strdup(n); }
    ~Person() { free(name); }
    // 無拷貝構造 → 淺拷貝
};
Person p1("Alice");
Person p2 = p1; // p2.name == p1.name
// p1、p2 析構時同一塊記憶體 free 兩次 → 崩潰

深拷貝正確實現

Person(const Person& other) {
    name = strdup(other.name); // 分配新記憶體並複製內容
}

四、拷貝賦值運算符

場景區別

  • 拷貝構造:新對象初始化。
  • 賦值運算符:已存在對象被覆寫(t2 = t1;)。

標準五步法(Copy-and-Swap 惯用法更優雅)

Person& operator=(const Person& rhs) {
    if (this == &rhs) return *this;       // 1. 自賦值檢查
    free(name);                           // 2. 釋放舊資源
    name = strdup(rhs.name);              // 3. 分配新資源 + 4. 複製內容
    return *this;                         // 5. 支援鏈式賦值
}

Copy-and-Swap 現代寫法(強異常安全)

Person& operator=(Person rhs) { // 參數按值傳遞 → 自動調用拷貝構造
    swap(*this, rhs);             // 交換內部指針
    return *this;                 // rhs 析構時釋放舊資源
}
friend void swap(Person& a, Person& b) noexcept {
    std::swap(a.name, b.name);
}

五、總結對照表

函數觸發時機預設行為資源管理類必須自定義?
預設構造無參創建對象不初始化成員是(初始化資源句柄)
析構對象銷毀不釋放資源是(釋放資源)
拷貝構造初始化新對象淺拷貝是(深拷貝)
賦值運算符已有對象賦值淺拷貝是(深拷貝 + 自賦值檢查)

六、最佳實踐建議

  1. 遵循 Rule of Three(C++98)/ Rule of Five(C++11,加移動構造/移動賦值):若需自定義其中任一,通常應全部自定義。
  2. 優先使用 std::unique_ptr/shared_ptr/string/vector 等 RAII 類型替代裸指針,讓編譯器生成的預設版本自動正確(Rule of Zero)。
  3. 賦值運算符採用 Copy-and-Swap,碼簡單、強異常安全、自動處理自賦值。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved