好问题 👍,这个就是理解 = 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 同一块内存 → 崩溃
这个例子非常直观,用一个指针就能理解浅拷贝、深拷贝的区别。
如果你需要,我可以帮你画一张 指针指向的内存图,形象展示浅拷贝和深拷贝的区别,让你一眼就明白。
你想让我画吗?