如何理解 C++ 中的深拷贝和浅拷贝?

如何理解 C++ 中的深拷贝和浅拷贝?


回答一:
这个问题其实答案很简单,而且基本上每一本C++书都会提及,但是或许对于初学者并不那么容易的理解,所以我想尝试以我的比喻来讲解。我认为浅拷贝是一个不喜欢思考的懒汉,而深拷贝则是一个思维严谨,喜欢思考的人。对于懒汉来说,虽然给了他任务,但是他总是想尽量的少做一些事情,所以很多时候做出来的东西就是只看到了表面,不会去思考对不对。

struct X
{
  int x;
  int y;
};

对于懒汉来说,他很直白的看到了x,看到了y,然后就拷贝x和y,然后就不管了,反正我完成我的拷贝了,至于对不对,我不管。

而一旦有了引用或者指针,事情就不一样了

struct X
{
  int x;
  int y;
  int* p;
};

懒汉依然只是直接表面级别的拷贝,于是拷贝x, y , p,但是他没有思考接下来的事情对不对。对于指针或者引用来说,若是只是拷贝表面,那么拷贝后的物体的指针也和原来的指针指向的是同一个对象,所以虽然目的想完成一个完美的克隆体,但是却发现克隆体和原来的物体中间还有一根线连着,没有完美的分离。

int *p = new int(47);
int *q = p;

如q与p都是指向一个物体一样。那么如果原来的物体销毁了,但是现在拷贝的物体还在,那么这时候你拷贝后的物体的成员指针就是一个悬挂指针,指向了不再存在的物体,那么你访问的话,那就不知道会发生什么了。而对于深拷贝,这一个勤奋的人,他不会只做表面,他会把每一个细节都照顾好。于是,当他遇到指针的时候,他会知道new出来一块新的内存,然后把原来指针指向的值拿过来,这样才是真正的完成了克隆体和原来的物体的完美分离, 如果物体比作人的话,那么原来的人的每一根毛细血管都被完美的拷贝了过来,而绝非只是表面。所以,这样的代价会比浅拷贝耗费的精力更大,付出的努力更多,但是是值得的。当原来的物体销毁后,克隆体也可以活的很好。然而事实上是这个世界上大多都是懒汉,包括编程的人,编译器等,所以默认的行为都是浅拷贝,于是有时候你需要做一个勤奋的人,让事情做正确,自己去完成深拷贝所需要的事情。
在这里插入图片描述

回答二:
我认为这样解释可能会做成一些误解。事实上,所谓的「浅拷贝」和「深拷贝」各自代表不同的意义,各有所需。关键是要区分值语意(value semantics)和引用语意(reference semantics)。

对于值语意的对象,在 x = y 完成复制之后,y 的状态改变不能影响到 x,这特性称为独立性(independence)。使用深拷贝的方式可以完全复制一个独立于原来的对象。C++ 提供的(模板)类大部分都是值语意的,如 std::basic_string、std::vector 等。

有些对象会引用相同的对象,举个游戏中的例子。通常多个模型可以引用同一个材质,复制模型时并不会深度复制新一份材质。如果需要改变个别模型的材质里的参数,才会手动把该模型的材质复制,独立于其他模型的材质。

上面所讲的其实不是 C++ 特有的问题。Java、C# 等语言在设计一个类的时候都要考虑到这些语意。而C++比较麻烦的地方,就是不支持垃圾回收,需要处理生命周期问题。

对于引用语意,在 C++ 中除了可以用原始指针,还可以使用如 std::shared_ptr、std::weak_ptr 等智能指针来表示引用,这样便可以更轻松地使用引用语意。例如蓝色的例子可改为:

struct X 
{ 
	int x; 
	int y; 
	std::shared_ptr<int> p; 
};

这里假设 p 是用引用语意,X b = a 浅拷贝之后,a.p 和 b.p 是引用同一个 int 变量。

我重申,值语意和引用语意是两种不同的需求。指针成员变量也不一定代表是引用语意,例如 vector 里的缓冲区便是以指针储存,但 vector 是值语意的。

「编译器等……默认的行为都是浅拷贝」的原因之一,是深拷贝不一定能实现。例如,指向的对象可能是多态的(C++没有标准的虚构造函数),也可能是数组,也可能有循环引用(如 struct N { N* p; };)。所以只能留待成员变量的类来决定怎样实现复制。 值得一提的是,除了复制操作,还可以考虑移动和交换操作。它们的性能通常比复制操作更优。自C++11 开始也提供了标准的移动操作实现方法。

回答三:
我吐个槽。。这玩意儿根本就是胡乱扯出来的概念,不是说不好,只是把很自然的事情搞一个高大上的名词,感觉故弄玄虚。说白了,就是类值和类指针行为的区别。对于含有指针成员的类,直接拷贝可能会出现两个对象的指针成员指向同一个数据区。这时候一般先new个内存,然后复制内容 当然这也得看情况,如果整个类只需要一份这样的数据,就没必要new内存了,直接编译器默认构造函数就行。总结:一般对于堆上的内存才需要深拷贝 这玩意儿完全是和你的程序设计有关 。整个概念还迷糊人 吐槽完毕。。请拍砖

回答四:
浅拷贝是引用答案,深拷贝是抄袭狗…

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值