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

C++ Primer 筆記:構造函數與拷貝構造(深拷貝 vs 淺拷貝)

     編輯:C++ 入門

構造函數與拷貝控制是 C++ 類設計的基石。本文通過實例講解構造函數特性、淺拷貝陷阱、深拷貝實現及現代 C++ 最佳實踐。

1. 構造函數基礎

  • 同名於類,無返回類型,用於初始化對象成員
  • 可重載,通過參數列表區分調用版本
  • 不能聲明為 const 或 virtual(析構函數可虛)
  • 若用戶聲明了任意構造函數,編譯器不再生成默認構造函數
  • 初始化列表:唯一初始化 const、引用成員及基類子對象的方式,性能優於構造函數體內賦值

2. 淺拷貝缺陷演示

類包含指針成員時,默認拷貝構造函數執行逐位拷貝(淺拷貝),導致多個對象共享同一塊堆內存:

class Person {
public:
    Person(int n, const string& str) : age(n), name(new string(str)) {}
    ~Person() { delete name; }
private:
    int age;
    string* name;  // 動態分配
};

int main() {
    Person p1(10, "SCOTT");
    Person p2 = p1;  // 調用默認拷貝構造:淺拷貝 name 指針
    return 0;  // p2、p1 依次析構,雙重 delete 同一地址 → 崩潰
}

運行結果:

Init Person
~Person name: SCOTT age: 10
Segmentation fault (core dumped)

3. 深拷貝正確實現

自定義拷貝構造函數,為指針成員分配新內存並複製內容:

Person(const Person& other) {
    if (other.name) {
        name = new string(*other.name);  // 深拷貝字符串內容
        age = other.age;
    } else {
        name = nullptr;
        age = 0;
    }
}

現在每個對象擁有獨立的 string 堆內存,析構時互不干擾。

4. 三/五法則

C++98 三法則C++11 五法則說明
拷貝構造函數拷貝構造函數深拷貝資源
拷貝賦值運算符拷貝賦值運算符釋放舊資源,深拷貝新資源
析構函數析構函數釋放資源
移動構造函數轉移資源所有權(noexcept)
移動賦值運算符轉移資源所有權(noexcept)

原則:若類管理資源(堆內存、文件句柄、Socket 等),必須顯式實現上述操作;否則改用智能指針(unique_ptr/shared_ptr)或標準容器,讓編譯器合成的操作自動正確(Rule of Zero)。

5. 現代 C++ 最佳實踐

class Person {
public:
    Person(int n, string str) : age(n), name(std::make_unique<string>(std::move(str))) {}
    // 無需顯式聲明拷貝/移動/析構,unique_ptr 自動管理
    // 若需拷貝語義,改用 shared_ptr 或實現深拷貝
private:
    int age;
    std::unique_ptr<string> name;  // 單一所有權,禁止拷貝,允許移動
};
  • 優先使用 std::unique_ptr 表達獨占所有權,std::shared_ptr 表達共享所有權
  • 避免裸指針管理資源,將資源封裝在 RAII 類中
  • 若確需自定義拷貝,參考 Copy-and-swap 慣用語 實現強異常安全

6. 總結

  1. 淺拷貝只複製指針值,導致多重釋放或數據競爭
  2. 深拷貝分配新內存並複製內容,保證對象獨立性
  3. C++11 移動語義讓資源轉移高效安全,結合智能指針可大幅減少樣板代碼
  4. 理解對象生命週期與所有權模型,是寫出健壯 C++ 代碼的關鍵
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved