三亩地 三亩地SAN MU DI · CODE DIARY
ARTICLE DETAIL

日记详情

真实记录编程学习的某一天,欢迎挑你感兴趣的翻一翻。

小学生学C++编程语法知识(C++中的深拷贝与浅拷贝)

小学生学C++编程语法知识(C++中的深拷贝与浅拷贝)


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 q

p和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。


四、浅拷贝有什么问题?

看代码:

#include<iostream> using namespace std; class Student { public: int *score; Student() { score = new int(100); } ~Student() { delete score; } }; int main() { Student a; Student b = a; }

发生什么?

创建a:

a.score | ↓ 100

复制b:

a.score | ↓ 100 ↑ | b.score

程序结束:

先销毁b:

delete score;

100被释放。


然后销毁a:

再次:

delete score;

可是:

这个内存已经没有了!

这叫:

重复释放


可能导致:

  • 程序崩溃

  • 内存错误


五、什么是深拷贝?

深拷贝:

一句话:

不仅复制地址,而是重新复制一份数据。


还是刚才例子:

a:

a.score | ↓ 100

复制:

Student b=a;

深拷贝以后:

a.score | ↓ ┌─────┐ │100 │ └─────┘ b.score | ↓ ┌─────┐ │100 │ └─────┘

注意:

两个100:

是两块不同的内存。


六、如何实现深拷贝?

需要自己写:

拷贝构造函数


格式:

类名(const 类名 &对象) { }

例如:

#include<iostream> 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 b=a; }

关键:

score = new int(*s.score);

拆开:

第一步:

取出原来的值:

*s.score

得到:

100

第二步:

重新申请空间:

new int(100)

得到新的:

100

所以:

两个对象:

互不影响。


七、浅拷贝 vs 深拷贝

比较浅拷贝深拷贝
复制什么地址数据
速度
是否申请新空间
两个对象是否共享数据
容易出错吗容易安全
需要自己写吗默认提供通常自己写

八、生活中的比喻

浅拷贝:

小明有一个遥控汽车。

小红复制:

拿了一张纸:

写:

遥控汽车的位置:小明家

这不是复制汽车。

只是复制地址。


深拷贝:

小红:

重新买了一辆一模一样的汽车。

现在:

小明玩自己的。

小红玩自己的。

互不影响。


九、什么时候需要深拷贝?

主要情况:

类里面有指针

例如:

class Array { int *data; };

或者:

动态数组:

int *arr=new int[100];

或者:

字符串:

char *name;

都需要考虑深拷贝。


十、C++中的“三件套”

如果一个类管理动态内存:

通常需要:

1. 构造函数

申请空间:

new

2. 拷贝构造函数

负责:

深拷贝

3. 析构函数

释放空间:

delete

这三个叫:

Rule of Three(三法则)


十一、给同学们的记忆口诀

普通变量,直接复制。 指针变量,要看地址。 复制地址,是浅拷贝。 复制数据,是深拷贝。 new出来的东西, 一定要小心释放。

十二、信奥竞赛中的理解

在GESP、CSP-J阶段:

通常:

  • int

  • double

  • char

  • string

  • vector

已经帮我们处理好了。

例如:

vector<int> a; vector<int> b=a;

这里:

不是简单复制地址。

vector内部已经实现了深拷贝。

所以:

b[0]=100;

不会影响:

a[0]

但是学习:

  • 链表

  • 二叉树

  • 动态数组

  • 智能指针

时:

深浅拷贝就是必须掌握的核心知识。


一句话总结:

浅拷贝复制“钥匙”(地址),深拷贝复制“房子”(数据)。
指针越多,越要小心浅拷贝。


← 返回列表