碼世界的「克隆術」:深拷貝vs淺拷貝

2024.02.06


一、什麼是拷貝?

在C++程式設計中,拷貝是將一個物件的值複製到另一個物件的過程。這看似簡單的操作卻牽涉到深拷貝和淺拷貝兩種不同的實作方式。我們從淺拷貝開始,看看它是如何運作的。

二、淺拷貝:表面上的複製

淺拷貝是一種簡單的複製方式,它只複製物件的值,包括物件中的基本資料類型和指標。在淺拷貝中,兩個物件共享相同的記憶體空間,這可能導致潛在的問題,尤其是在物件包含動態分配記憶體時。


// 示例:浅拷贝
#include <iostream>
class ShallowCopyExample {
public:
    int* data;
    ShallowCopyExample(const ShallowCopyExample& other) {
        // 浅拷贝
        data = other.data;
    }
    void DisplayData() {
        std::cout << "Data: " << *data << std::endl;
    }
};

int main() {
    ShallowCopyExample obj1;
    obj1.data = new int(42);
    ShallowCopyExample obj2 = obj1; // 浅拷贝
    obj1.DisplayData(); // 输出:Data: 42
    obj2.DisplayData(); // 输出:Data: 42
    // 修改obj1的data
    *obj1.data = 99;
    obj1.DisplayData(); // 输出:Data: 99
    obj2.DisplayData(); // 输出:Data: 99,这里也发生了变化!
    // 注意:由于浅拷贝,obj1和obj2共享相同的data指针,导致一个变化另一个也跟着变化
    delete obj1.data;
    // 注意:由于浅拷贝,删除obj1的data后,obj2的data指针成为了悬空指针,可能导致未定义行为
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.

在這個例子中,兩個物件obj1和obj2透過淺拷貝共享了相同的data指標。修改其中一個物件的data會影響另一個對象,同時在釋放記憶體時需要格外小心,避免懸空指標的問題。

三、深拷貝:複製的完整性

相對於淺拷貝,深拷貝會複製物件的所有內容,包括指標所指向的記憶體。這樣,每個物件都有自己的一份獨立的資料副本,互不影響。


// 示例:深拷贝
#include <iostream>
class DeepCopyExample {
public:
    int* data;
    DeepCopyExample(const DeepCopyExample& other) {
        // 深拷贝
        data = new int(*other.data);
    }
    ~DeepCopyExample() {
        // 注意:需要手动释放动态分配的内存
        delete data;
    }
    void DisplayData() {
        std::cout << "Data: " << *data << std::endl;
    }
};
int main() {
    DeepCopyExample obj1;
    obj1.data = new int(42);
    DeepCopyExample obj2 = obj1; // 深拷贝
    obj1.DisplayData(); // 输出:Data: 42
    obj2.DisplayData(); // 输出:Data: 42
    // 修改obj1的data
    *obj1.data = 99;
    obj1.DisplayData(); // 输出:Data: 99
    obj2.DisplayData(); // 输出:Data: 42,这里没有变化!
    // 注意:由于深拷贝,obj1和obj2拥有独立的data指针,互不影响
    delete obj1.data;
    return 0;
}
  • 1.
  • 2.
  • 3.
  • 4.
  • 5.
  • 6.
  • 7.
  • 8.
  • 9.
  • 10.
  • 11.
  • 12.
  • 13.
  • 14.
  • 15.
  • 16.
  • 17.
  • 18.
  • 19.
  • 20.
  • 21.
  • 22.
  • 23.
  • 24.
  • 25.
  • 26.
  • 27.
  • 28.
  • 29.
  • 30.
  • 31.
  • 32.

在深拷貝的範例中,每個物件都有自己的data指標和對應的記憶體。這樣的設計確保了物件之間的獨立性,防止了因為資料共享而引發的問題。

四、如何選擇:深拷貝還是淺拷貝?

選擇深拷貝還是淺拷貝取決於具體的需求和設計。在某些情況下,淺拷貝可能是合適的,尤其是當物件沒有動態分配記憶體或共享資料是期望的行為時。然而,如果物件包含指針,或需要在不同物件之間保持獨立性,深拷貝是更安全的選擇。

五、注意事項:動態分配記憶體的釋放

使用深拷貝時,要格外注意動態分配的內存,確保在物件生命週期結束時進行適當的釋放。在上述深拷貝範例中,我們使用了析構函數來釋放data指向的記憶體。

六、總結:靈活運用拷貝方式

深拷貝和淺拷貝在C++中都有其適用的場景,理解它們的原理和使用方式有助於我們更靈活地運用在實際程式設計中。透過選擇合適的拷貝方式,我們可以更好地管理數據,確保程式的正確性和效能。