好問題 👍,這個就是理解 = delete
的關鍵前置知識。
我從 C語言基礎 出發,像教科書一樣,從零講給你聽。
1. 什么是“拷貝”?
在 C++ 中,當你有一個對象 A
,然后寫:
Device_Info a;
Device_Info b = a; // 或者 b(a);
這就是在“拷貝一個對象”。
系統會幫你把 a
的內容復制到 b
里,這個過程叫做 拷貝構造。
同樣地,如果寫:
Device_Info c;
c = a; // 已經存在的對象 c,用 a 的內容覆蓋它
這就是“拷貝賦值”。
2. 淺拷貝(Shallow Copy)
👉 定義:只復制“表面數據”,也就是 成員變量的值。
舉個例子:
struct Data {int* ptr;
};Data a;
a.ptr = new int(42); // a 里面的指針指向堆上的一個整數Data b = a; // 系統默認的拷貝(淺拷貝)
這時候 a.ptr
和 b.ptr
指向同一塊內存!
- 修改
*a.ptr
會影響*b.ptr
- 如果兩個對象都去
delete ptr;
→ 就會出現 二次釋放(double free) 錯誤。
👉 這就是淺拷貝的風險。
3. 深拷貝(Deep Copy)
👉 定義:不光復制指針的值,還會 分配一塊新的內存,并復制數據內容。
改寫上面的例子:
struct Data {int* ptr;// 自己實現拷貝構造函數(深拷貝)Data(const Data& other) {ptr = new int(*other.ptr); // 新開一塊內存,復制內容}
};
這樣 a.ptr
和 b.ptr
各自獨立:
- 改變
a
不會影響b
- 各自釋放時也不會沖突。
4. 為什么要禁用拷貝?(= delete)
有些類(比如你說的 Device_Info
)是全局單例,不能被“復制”出新對象。
如果你允許淺拷貝:
- 很可能復制出來多個“全局唯一”的對象,狀態混亂。
如果你允許深拷貝:
- 邏輯上也沒意義:全局設備信息只有一份,復制出來的副本沒法保持同步。
👉 所以直接禁用拷貝:
Device_Info(const Device_Info&) = delete; // 禁用拷貝構造
Device_Info& operator=(const Device_Info&) = delete; // 禁用拷貝賦值
這樣一來,編譯器遇到下面的代碼時就會報錯:
Device_Info a;
Device_Info b = a; // ? 編譯錯誤
Device_Info c;
c = a; // ? 編譯錯誤
保證了你的 Device_Info
只有一個實例,不會被復制。
? 一句話總結:
- 淺拷貝 = 復制指針值 → 多個對象共享同一資源(風險大)。
- 深拷貝 = 復制指針指向的內容 → 每個對象都有獨立資源(安全,但開銷大)。
- = delete = 禁止拷貝 → 防止“本不該復制的對象”被復制(最安全)。
要不要我給你畫個“淺拷貝 vs 深拷貝”的圖解,形象一點?
好👌 那我從 C 角度 慢慢過渡到 C++ 淺拷貝 / 深拷貝,讓你完全能理解。
1. C 里拷貝變量
在 C 里,我們寫:
int a = 10;
int b = a; // 把 a 的值拷貝到 b
這里 b
得到的是一個獨立的副本,修改 b
不會影響 a
。
這就相當于 值的復制。
2. 指針和數組的情況
再來看指針或數組:
char *p = malloc(10);
char *q = p; // q 和 p 指向同一塊內存
現在 p
和 q
共享一塊內存。如果你通過 q
修改數據,p
看到的也會變。
這就不是復制內容,而是復制了“地址” → 淺拷貝。
3. 到 C++ 的對象
在 C++ 里,對象可以有很多成員(比如內存指針、數組、文件句柄等)。
當你寫:
Device_Info a;
Device_Info b = a; // 調用拷貝構造函數
編譯器默認會幫你“一個字段一個字段”地復制 → 淺拷貝。
- 如果對象里有普通的 int、double,這沒問題。
- 如果對象里有指針,那就危險了!因為兩個對象的指針會指向同一塊內存,可能導致 二次釋放 (double free)、懸空指針。
4. 淺拷貝 vs 深拷貝
淺拷貝(shallow copy)
只復制“指針的值”,不復制指針指向的內容。
結果:多個對象共享同一塊資源。
例子:
struct A {int *data;
};
A a;
a.data = new int(5);A b = a; // 淺拷貝:b.data 和 a.data 指向同一塊內存
這樣當 a
和 b
都 delete data
時就會崩潰。
深拷貝(deep copy)
不但復制指針的值,還復制一份獨立的內容。
結果:每個對象有自己獨立的資源。
例子:
struct A {int *data;// 自己實現拷貝構造函數A(const A& other) {data = new int(*other.data); // 復制一份內容}
};
這樣 a
和 b
各自有自己的內存,互不影響。
5. 為什么要 = delete
有時候我們覺得拷貝很危險(比如全局唯一的資源),
干脆禁止拷貝,寫:
Device_Info& operator=(const Device_Info&) = delete;
意思是:禁止拷貝賦值,編譯器一旦遇到 a = b
就會報錯。
這就是一種防止“淺拷貝出錯”的安全寫法。
📌 總結:
- 淺拷貝:只復制指針本身,多個對象共享一塊資源 → 可能危險。
- 深拷貝:復制指針指向的內容,每個對象有自己的資源 → 安全但需要寫代碼實現。
- 禁用拷貝 (
= delete
):當資源不能被復制時(比如全局唯一對象),直接禁止。
要不要我給你寫一個小的 C 風格 vs C++ 淺拷貝 vs 深拷貝的完整對比代碼,你直接跑一下就能看到區別?
深拷貝案例
好的,我們用一個 非常簡單的例子 來說明淺拷貝和深拷貝的區別,不涉及圖像,只用一個 int*
指針就夠了。
例子:管理一個整數的類
#include <iostream>class MyInt {
public:int* value;// 構造函數MyInt(int v) {value = new int(v);}// 析構函數~MyInt() {delete value;}// 深拷貝構造函數MyInt(const MyInt& other) {value = new int(*other.value); // 申請新內存,復制內容}void print() {std::cout << *value << std::endl;}
};int main() {MyInt a(10);MyInt b = a; // 調用深拷貝構造函數a.print(); // 10b.print(); // 10*a.value = 20; // 修改 aa.print(); // 20b.print(); // 10(b 不受影響)return 0;
}
🔹 分析
a.value
和b.value
各自指向獨立內存- 修改
a.value
不會影響b.value
- 析構時安全,不會 double free
如果去掉深拷貝構造函數,默認的拷貝構造是淺拷貝:
MyInt(const MyInt& other) = default;
結果就是:
a.value
和b.value
指向同一塊內存- 修改
a
會影響b
- 兩個對象析構時都會 delete 同一塊內存 → 崩潰
這個例子非常直觀,用一個指針就能理解淺拷貝、深拷貝的區別。
如果你需要,我可以幫你畫一張 指針指向的內存圖,形象展示淺拷貝和深拷貝的區別,讓你一眼就明白。
你想讓我畫嗎?