在面向对象的编程中,经常会遇到对象的克隆,如果不理解深复制和浅复制(深拷贝和浅拷贝)的概念,你的代码就可能出现一些问题。
我们知道,对象是由一系列key,value的元素组成,不同的编程语言中,key,value的数据类型划分不太一样,有些语言所有数据都是对象,如python,有些则有其它的划分。但是基于数据或者对象的存储方式,基本可以把数据类型划分为值类型和引用类型。一般值类型的数据是存储在栈内存中,引用类型的数据是存放在堆内存中,如下图:
如果一个对象中的所有数据都是值类型的数据,则不会出现深复制和浅复制的问题,例如一个数组[1,2,3,4,5,6]无论怎么复制,都是重新一份数据。另一种情况是如果一个对象中存在引用类型的数据,那么,当复制这个对象的时候,对象当中的引用型数据是再复制一份,还是用原来的引用呢?这里就是深复制和浅复制的区别了,如果把对象中所有引用的数据再复制一遍,这个就是深复制,如果只是给引用再加个指针,则是浅复制。
由此,可以看到,深复制不受复制之前引用数据的影响,浅复制则不然,如果原来的引用型数据改变,则复制之后的数据也会接着改变。
首先来看一下Python的示例:
#!/usr/bin/env python import copy a=[1,2,3,4,5,6] b=['a','b','c',a] c=copy.copy(b) d=copy.deepcopy(b) print c #['a', 'b', 'c', [1, 2, 3, 4, 5, 6]] print d #['a', 'b', 'c', [1, 2, 3, 4, 5, 6]] a.append(7) print c #['a', 'b', 'c', [1, 2, 3, 4, 5, 6, 7]] print d #['a', 'b', 'c', [1, 2, 3, 4, 5, 6]]
可以看到c和d两个对象,一个是浅复制b,一个是深复制b,b对象中还有a,当a改变时,浅复制的对象c跟着改变了,而d却没有变。
Javascript的数据类型虽然和python有点区别(javascript把数据分为原始数据类型和引用数据类型,原始数据类型不是严格的对象,python所有的数据都是对象),但是javascript也有深复制和浅复制的概念,只是javascript没有提供类似的方法,下面是用javascript实现的深复制和浅复制:
<script> Object.prototype.copy=function(){ var newobj=new Object(); for (var i in this){ newobj[i]=this[i]; } return newobj; } var a={"name":"Hito","website":"www.hitoy.org"}; var b={"rank":1,"info":a}; var c=b.copy(); b.info.website="www.hitoy.com"; console.log(b.info.website); console.log(c.info.website); //b和c相等 b.rank=2; console.log(b.rank); console.log(c.rank); //b和c不相等 Object.prototype.deepcopy=function(){ var newobj=new Object(); for (var i in this){ if(typeof(this[i])=="object"){ newobj[i]=this[i].deepcopy(); }else{ newobj[i]=this[i]; } } return newobj; } var a={"name":"Hito","website":"www.hitoy.org"}; var b={"rank":1,"info":a}; var d=b.deepcopy(); b.info.website="www.hitoy.com"; console.log(b.info.website); console.log(d.info.website); //b和d不相等 </script>
和Javascript一样,php没有提供类似深复制和浅复制的函数,而且php的数据类型和python,Javascript比起来又有一些区别,PHP的数据类型可以简单划分成两类:标量类型和复合类型。PHP数据类型中的标量类型和复合类型中的数组可以看成是值类型,所有类的实例化对象则是引用型数据1, 再来看一下PHP如何实现深复制和浅复制:
<?php class A{ public $varb=1; } class B{ public $object1; } $b=new B(); $bb=new A(); $b->object1=$bb; $c=clone $b; print_r($b->object1); print_r($c->object1); //上面两个相等 $bb->varb=2; print_r($b->object1); print_r($c->object1); //上面两个也相等 class A{ public $varb=1; } class B{ public $object1; public function __clone(){ $this->object1= clone $this->object1; } } $b=new B(); $bb=new A(); $b->object1=$bb; $c=clone $b; print_r($b->object1); print_r($c->object1); //上面两个相等 $bb->varb=2; print_r($b->object1); print_r($c->object1); //上面不相等 ?>
1,此结论来源于测试,没有出处,欢迎纠正或补充。