深复制和浅复制

 分类: 致知

在面向对象的编程中,经常会遇到对象的克隆,如果不理解深复制和浅复制(深拷贝和浅拷贝)的概念,你的代码就可能出现一些问题。

我们知道,对象是由一系列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,此结论来源于测试,没有出处,欢迎纠正或补充。

发表回复