S SmartDocs
Series: C++ cpp 254 lines · Updated 2026-04-03

copy_control.cpp

C++/Part2_物件導向/Ch09_建構子與解構子/copy_control.cpp

// ============================================================
// Ch09 — 拷貝建構子、拷貝賦值運算子、深拷貝、Rule of Three
// 編譯:g++ -std=c++17 -Wall -o copy_control copy_control.cpp
// ============================================================

#include <iostream>
#include <algorithm>
#include <cstring>

// ============================================================
// DynamicArray — 管理原始動態陣列
//   完整實作 Rule of Three:
//   1. 解構子
//   2. 拷貝建構子
//   3. 拷貝賦值運算子
// ============================================================

class DynamicArray {
private:
    int* data;
    int size;
    std::string label;  // 用於輸出追蹤

    void printAction(const std::string& action) const {
        std::cout << "    [" << label << "] " << action << std::endl;
    }

public:
    // 建構子 — 配置記憶體並初始化
    DynamicArray(const std::string& label, int size, int initValue = 0)
        : data(new int[size]), size(size), label(label) {
        std::fill(data, data + size, initValue);
        printAction("建構子:配置 " + std::to_string(size) + " 個 int");
    }

    // ────────────────────────────────────────
    // Rule of Three (1/3):解構子
    // ────────────────────────────────────────
    ~DynamicArray() {
        printAction("解構子:釋放記憶體");
        delete[] data;
    }

    // ────────────────────────────────────────
    // Rule of Three (2/3):拷貝建構子(深拷貝)
    // ────────────────────────────────────────
    DynamicArray(const DynamicArray& other)
        : data(new int[other.size]),
          size(other.size),
          label(other.label + "_copy") {
        std::copy(other.data, other.data + size, data);
        printAction("拷貝建構子:深拷貝 " + std::to_string(size) + " 個元素");
    }

    // ────────────────────────────────────────
    // Rule of Three (3/3):拷貝賦值運算子
    //   使用 copy-and-swap 慣用手法
    // ────────────────────────────────────────
    DynamicArray& operator=(const DynamicArray& other) {
        printAction("拷貝賦值運算子");

        // 自我賦值檢查
        if (this == &other) {
            printAction("  偵測到自我賦值,跳過");
            return *this;
        }

        // 釋放舊資源
        delete[] data;

        // 配置新資源並複製
        size = other.size;
        data = new int[size];
        std::copy(other.data, other.data + size, data);
        // 保留原本的 label
        printAction("  深拷貝完成");

        return *this;
    }

    // 元素存取
    int& at(int index) {
        if (index < 0 || index >= size) {
            throw std::out_of_range("索引超出範圍");
        }
        return data[index];
    }

    int at(int index) const {
        if (index < 0 || index >= size) {
            throw std::out_of_range("索引超出範圍");
        }
        return data[index];
    }

    int getSize() const { return size; }
    const std::string& getLabel() const { return label; }

    // 印出所有元素
    void print() const {
        std::cout << "    [" << label << "] 大小=" << size << ", 內容=[";
        for (int i = 0; i < size; ++i) {
            if (i > 0) std::cout << ", ";
            std::cout << data[i];
        }
        std::cout << "]" << std::endl;
    }

    // 印出內部指標位址(用於驗證深拷貝)
    void printAddress() const {
        std::cout << "    [" << label << "] data 位址: " << data << std::endl;
    }
};

// ============================================================
// 展示淺拷貝的危險
// ============================================================

class ShallowCopyDemo {
private:
    int* value;

public:
    ShallowCopyDemo(int v) : value(new int(v)) {
        std::cout << "    [ShallowDemo] 建構:*value = " << *value
                  << ", 位址 = " << value << std::endl;
    }

    // 故意使用淺拷貝(錯誤示範!)
    // 編譯器自動生成的拷貝建構子就是這樣做的
    // ShallowCopyDemo(const ShallowCopyDemo& other) = default;
    // 等同於:value(other.value)  → 兩個物件指向同一塊記憶體!

    ~ShallowCopyDemo() {
        std::cout << "    [ShallowDemo] 解構:位址 = " << value << std::endl;
        // 如果兩個物件共享同一個指標,第二次 delete 就會 crash
        delete value;
    }

    int getValue() const { return *value; }
    void setValue(int v) { *value = v; }
};

// ============================================================
// main
// ============================================================

int main() {
    std::cout << "========================================" << std::endl;
    std::cout << " Ch09 拷貝控制 — 深拷貝 vs 淺拷貝" << std::endl;
    std::cout << "========================================\n" << std::endl;

    // ------ 1. 建立與基本操作 ------
    std::cout << "【1】建立 DynamicArray\n" << std::endl;

    DynamicArray arr1("arr1", 5, 10);
    arr1.print();

    arr1.at(0) = 100;
    arr1.at(2) = 200;
    arr1.at(4) = 300;
    std::cout << "  修改後:" << std::endl;
    arr1.print();
    std::cout << std::endl;

    // ------ 2. 拷貝建構子(深拷貝) ------
    std::cout << "【2】拷貝建構子(深拷貝)\n" << std::endl;

    DynamicArray arr2(arr1);  // 呼叫拷貝建構子
    std::cout << std::endl;

    arr1.print();
    arr2.print();
    std::cout << std::endl;

    // 驗證是不同的記憶體位址(深拷貝)
    std::cout << "  驗證深拷貝 — 記憶體位址不同:" << std::endl;
    arr1.printAddress();
    arr2.printAddress();
    std::cout << std::endl;

    // 修改 arr2 不影響 arr1
    std::cout << "  修改 arr2 後:" << std::endl;
    arr2.at(0) = 999;
    arr1.print();  // arr1 不受影響
    arr2.print();
    std::cout << std::endl;

    // ------ 3. 拷貝賦值運算子 ------
    std::cout << "【3】拷貝賦值運算子\n" << std::endl;

    DynamicArray arr3("arr3", 3, 0);
    arr3.print();
    std::cout << std::endl;

    arr3 = arr1;  // 呼叫拷貝賦值運算子
    std::cout << std::endl;
    arr3.print();
    arr1.print();
    std::cout << std::endl;

    // 修改 arr3 不影響 arr1
    arr3.at(0) = -1;
    std::cout << "  修改 arr3 後,arr1 不受影響:" << std::endl;
    arr3.print();
    arr1.print();
    std::cout << std::endl;

    // ------ 4. 自我賦值 ------
    std::cout << "【4】自我賦值測試\n" << std::endl;

    {
        DynamicArray& ref = arr1;
        ref = arr1;  // 透過參考進行自我賦值 — 應安全處理
    }
    arr1.print();
    std::cout << std::endl;

    // ------ 5. 淺拷貝的危險(概念說明) ------
    std::cout << "【5】淺拷貝的危險(概念說明)\n" << std::endl;

    std::cout << "  如果不實作深拷貝,以下情境會造成 double free:" << std::endl;
    std::cout << std::endl;
    std::cout << "    DynamicArray a(\"a\", 3);" << std::endl;
    std::cout << "    DynamicArray b = a;      // 淺拷貝 → a.data == b.data" << std::endl;
    std::cout << "    // b 解構 → delete[] data" << std::endl;
    std::cout << "    // a 解構 → delete[] data → 💥 double free!" << std::endl;
    std::cout << std::endl;
    std::cout << "  解決方案:實作深拷貝(配置新記憶體 + 複製內容)" << std::endl;
    std::cout << "  或使用 std::vector 等自動管理記憶體的容器(Rule of Zero)" << std::endl;
    std::cout << std::endl;

    // ------ 6. 解構順序示範 ------
    std::cout << "【6】解構順序示範\n" << std::endl;

    {
        std::cout << "  進入區塊:" << std::endl;
        DynamicArray local1("local1", 2, 1);
        DynamicArray local2("local2", 2, 2);
        DynamicArray local3("local3", 2, 3);
        local1.print();
        local2.print();
        local3.print();
        std::cout << "\n  離開區塊(解構順序 = 建構的相反順序 LIFO):" << std::endl;
    }

    std::cout << "\n  區塊已結束,local 物件已全部銷毀" << std::endl;

    std::cout << "\n========================================" << std::endl;
    std::cout << " 範例結束(arr1, arr2, arr3 即將解構)" << std::endl;
    std::cout << "========================================" << std::endl;

    return 0;
}

Related articles