)
C中的深拷贝与浅拷贝——给同学们讲一下“复制魔法”同学们今天我们学习一个非常重要的C知识当我们复制一个对象时到底是复制“外表”还是复制“里面的东西”这就是浅拷贝Shallow Copy深拷贝Deep Copy它们主要和指针动态内存new类对象复制有关。一、先理解什么叫“拷贝”生活中小明有一本《C魔法书》。小红说“我也想要一本一样的。”于是小明复制了一本。这就是复制 拷贝C里面Student a; Student b a;意思把对象a复制给对象b。例如int a 10; int b a;结果a: 10 b: 10两个数字互不影响。如果b 20;那么a: 10 b: 20没有问题。因为int里面保存的是数据本身。二、问题出现指针看一个例子int a 100; int *p a;内存a ┌─────┐ │100 │ └─────┘ ↑ | p指针p保存的是地址不是100。现在int *q p;发生什么100 ┌─────┐ │ │ └─────┘ ↑ ↑ | | p qp和q指向同一个地方。这就是浅拷贝三、什么是浅拷贝一句话只复制地址不复制真正的数据。比如class Student { public: int age; int *score; };创建Student s1; s1.age 10; s1.score new int(100);内存s1 age ┌────┐ │10 │ └────┘ score | ↓ ┌─────┐ │100 │ └─────┘现在Student s2 s1;默认拷贝C会这样s2.age s1.age s2.score s1.score结果s1.score | ↓ ┌─────┐ │100 │ └─────┘ ↑ | s2.score两个指针指向同一个100。四、浅拷贝有什么问题看代码#includeiostream using namespace std; class Student { public: int *score; Student() { score new int(100); } ~Student() { delete score; } }; int main() { Student a; Student b a; }发生什么创建aa.score | ↓ 100复制ba.score | ↓ 100 ↑ | b.score程序结束先销毁bdelete score;100被释放。然后销毁a再次delete score;可是这个内存已经没有了这叫重复释放可能导致程序崩溃内存错误五、什么是深拷贝深拷贝一句话不仅复制地址而是重新复制一份数据。还是刚才例子a:a.score | ↓ 100复制Student ba;深拷贝以后a.score | ↓ ┌─────┐ │100 │ └─────┘ b.score | ↓ ┌─────┐ │100 │ └─────┘注意两个100是两块不同的内存。六、如何实现深拷贝需要自己写拷贝构造函数格式类名(const 类名 对象) { }例如#includeiostream using namespace std; class Student { public: int *score; Student() { score new int(100); } //拷贝构造函数 Student(const Student s) { score new int(*s.score); } ~Student() { delete score; } }; int main() { Student a; Student ba; }关键score new int(*s.score);拆开第一步取出原来的值*s.score得到100第二步重新申请空间new int(100)得到新的100所以两个对象互不影响。七、浅拷贝 vs 深拷贝比较浅拷贝深拷贝复制什么地址数据速度快慢是否申请新空间否是两个对象是否共享数据是否容易出错吗容易安全需要自己写吗默认提供通常自己写八、生活中的比喻浅拷贝小明有一个遥控汽车。小红复制拿了一张纸写遥控汽车的位置小明家这不是复制汽车。只是复制地址。深拷贝小红重新买了一辆一模一样的汽车。现在小明玩自己的。小红玩自己的。互不影响。九、什么时候需要深拷贝主要情况类里面有指针例如class Array { int *data; };或者动态数组int *arrnew int[100];或者字符串char *name;都需要考虑深拷贝。十、C中的“三件套”如果一个类管理动态内存通常需要1. 构造函数申请空间new2. 拷贝构造函数负责深拷贝3. 析构函数释放空间delete这三个叫Rule of Three三法则十一、给同学们的记忆口诀普通变量直接复制。 指针变量要看地址。 复制地址是浅拷贝。 复制数据是深拷贝。 new出来的东西 一定要小心释放。十二、信奥竞赛中的理解在GESP、CSP-J阶段通常intdoublecharstringvector已经帮我们处理好了。例如vectorint a; vectorint ba;这里不是简单复制地址。vector内部已经实现了深拷贝。所以b[0]100;不会影响a[0]但是学习链表二叉树动态数组智能指针时深浅拷贝就是必须掌握的核心知识。一句话总结浅拷贝复制“钥匙”地址深拷贝复制“房子”数据。指针越多越要小心浅拷贝。