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

C++ 拷貝建構函式深度理解:淺拷貝陷阱與深拷貝實作

     編輯:C++ 入門

問題場景:預設拷貝的災難

#include <cstring>
using namespace std;

class Name {
    char *str;
public:
    Name(const char *n) {
        str = new char[strlen(n) + 1];
        strcpy(str, n);
    }
    ~Name() { delete[] str; }
};

int main() {
    Name a("peter");
    if (true) {
        Name b = a;  // 呼叫預設拷貝建構(淺拷貝)
    }  // b 解構,釋放 str 記憶體
    // a 解構,再次釋放同一記憶體 → 雙重釋放崩潰!
    return 0;
}

錯誤分析

階段預設拷貝行為後果
建構 a分配記憶體,str 指向 "peter"正常
拷貝建構 b逐位元複製 str 指標值a.str == b.str,指向同一塊記憶體
b 解構delete[] str記憶體被釋放,a.str 變成野指標
a 解構delete[] str雙重釋放,未定義行為,程式崩潰

解決方案:深拷貝建構函式

#include <cstring>
using namespace std;

class Name {
    char *str;
public:
    // 一般建構
    Name(const char *n) {
        str = new char[strlen(n) + 1];
        strcpy(str, n);
    }
    
    // 深拷貝建構函式
    Name(const Name& other) {
        str = new char[strlen(other.str) + 1];
        strcpy(str, other.str);  // 分配新記憶體並複製內容
    }
    
    ~Name() { delete[] str; }
};

int main() {
    Name a("peter");
    if (true) {
        Name b = a;  // 呼叫深拷貝建構
    }  // b 解構,釋放自己的記憶體
    // a 解構,釋放自己的記憶體 → 正常!
    return 0;
}

執行流程對比

【深拷貝版本】
Name a("peter");     // 建構 1 次
Name b = a;          // 拷貝建構 1 次(分配新記憶體)
}                    // b 解構 1 次(釋放 b.str)
}                    // a 解構 1 次(釋放 a.str)
總計:建構 2 次,解構 2 次,記憶體獨立管理,零錯誤

三大法則 / 五大法則

若類別管理資源(動態記憶體、檔案控制代、Socket 等),必須自定義:

C++98 三大法則

  1. 拷貝建構函式
  2. 拷貝賦值運算子
  3. 解構函式

C++11 五大法則(+ 移動語義)

  1. 拷貝建構函式
  2. 拷貝賦值運算子
  3. 移動建構函式
  4. 移動賦值運算子
  5. 解構函式

完整實作範例(含賦值運算子)

class Name {
    char *str;
public:
    Name(const char *n) : str(nullptr) {
        if (n) {
            str = new char[strlen(n) + 1];
            strcpy(str, n);
        }
    }
    
    // 拷貝建構
    Name(const Name& other) : str(nullptr) {
        if (other.str) {
            str = new char[strlen(other.str) + 1];
            strcpy(str, other.str);
        }
    }
    
    // 拷貝賦值(複製並交換慣用語)
    Name& operator=(Name other) {  // 參數傳值,自動拷貝
        swap(str, other.str);        // 交換資源
        return *this;                // other 解構時釋放舊資源
    }
    
    // 移動建構 (C++11)
    Name(Name&& other) noexcept : str(other.str) {
        other.str = nullptr;
    }
    
    // 移動賦值 (C++11)
    Name& operator=(Name&& other) noexcept {
        if (this != &other) {
            delete[] str;
            str = other.str;
            other.str = nullptr;
        }
        return *this;
    }
    
    ~Name() { delete[] str; }
};

現代 C++ 最佳實務

  1. 優先使用智慧指標:std::unique_ptr/shared_ptr 自動管理記憶體,編譯器自動生成正確的拷貝/移動語義
  2. 使用標準容器:std::string, std::vector 內建深拷貝
  3. 遵循 Rule of Zero:不手動管理資源,讓編譯器生成預設特殊函式

現代寫法


總結

拷貝建構函式是 C++ 資源管理的核心。淺拷貝導致雙重釋放是經典災難。掌握深拷貝實作、複製並交換慣用語、移動語義與Rule of Zero現代實務,能從根本上避免記憶體管理錯誤,寫出安全高效的 C++ 程式碼。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved