先说一下深拷贝和浅拷贝通俗理解
深拷贝: 赋值时值完全复制, 完全的 copy, 对其中一个作出改变, 不会影响另一个
浅拷贝: 赋值时, 引用赋值, 相当于取了一个别名. 对其中一个修改, 会影响另一个
PHP 中, = 赋值时, 普通对象是深拷贝, 但对对象来说, 是浅拷贝. 也就是说, 对象的赋值是引用赋值.(对象作为参数传递时, 也是引用传递, 无论函数定义时参数前面是否有 & 符号)
php4 中, 对象的 = 赋值是实现一份副本, 这样存在很多问题, 在不知不觉中我们可能会拷贝很多份副本.
php5 中, 对象的 = 赋值和传递都是引用. 要想实现拷贝副本, PHP 提供了 clone 函数实现.
clone 完全 copy 了一份副本. 但是 clone 时, 我们可能不希望 copy 源对象的所有内容, 那我们可以利用__clone 来操作.
在__clone() 中, 我们可以进行一些操作. 注意, 这些操作, 也就是__clone 函数是作用于拷贝的副本对象上的
- // 普通对象赋值, 深拷贝, 完全值复制
- $m = 1;
- $n = $m;
- $n = 2;
- echo $m;// 值复制, 对新对象的改变不会对 m 作出改变, 输出 1. 深拷贝
- echo PHP_EOL;
- /*==================*/
- // 对象赋值, 浅拷贝, 引用赋值
- class Test{
- public $a=1;
- }
- $m = new Test();
- $n = $m;// 引用赋值
- $m->a = 2;// 修改 m,n 也随之改变
- echo $n->a;// 输出 2, 浅拷贝
- echo PHP_EOL;
- ?>
由于对象的赋值时引用, 要想实现值复制, PHP 提供了 clone 函数来实现复制对象.
但是 clone 函数存在这么一个问题, 克隆对象时, 原对象的普通属性能值复制, 但是源对象的对象属性赋值时还是引用赋值, 浅拷贝.
- class Test{
- public $a=1;
- }
- class TestOne{
- public $b=1;
- public $obj;
- // 包含了一个对象属性, clone 时, 它会是浅拷贝
- public function __construct(){
- $this->obj = new Test();
- }
- }
- $m = new TestOne();
- $n = $m;// 这是完全的浅拷贝, 无论普通属性还是对象属性
- $p = clone $m;
- // 普通属性实现了深拷贝, 改变普通属性 b, 不会对源对象有影响
- $p->b = 2;
- echo $m->b;// 输出原来的 1
- echo PHP_EOL;
- // 对象属性是浅拷贝, 改变对象属性中的 a, 源对象 m 中的对象属性中 a 也改变
- $p->obj->a = 3;
- echo $m->obj->a;// 输出 3, 随新对象改变
- ?>
要想实现对象真正的深拷贝, 有下面两种方法:
写 clone 函数: 如下
- class Test{
- public $a=1;
- }
- class TestOne{
- public $b=1;
- public $obj;
- // 包含了一个对象属性, clone 时, 它会是浅拷贝
- public function __construct(){
- $this->obj = new Test();
- }
- // 方法一: 重写 clone 函数
- public function __clone(){
- $this->obj = clone $this->obj;
- }
- }
- $m = new TestOne();
- $n = clone $m;
- $n->b = 2;
- echo $m->b;// 输出原来的 1
- echo PHP_EOL;
- // 可以看到, 普通属性实现了深拷贝, 改变普通属性 b, 不会对源对象有影响
- // 由于改写了 clone 函数, 现在对象属性也实现了真正的深拷贝, 对新对象的改变, 不会影响源对象
- $n->obj->a = 3;
- echo $m->obj->a;// 输出 1, 不随新对象改变, 还是保持了原来的属性
- ?>
改写__clone() 函数不太方便, 而且你得在每个类中把这个类里面的对象属性都在__clone() 中 一一 clone
第二种方法, 利用序列化反序列化实现, 这种方法实现对象的深拷贝简单, 不需要修改类
- class Test{
- public $a=1;
- }
- class TestOne{
- public $b=1;
- public $obj;
- // 包含了一个对象属性, clone 时, 它会是浅拷贝
- public function __construct(){
- $this->obj = new Test();
- }
- }
- $m = new TestOne();
- // 方法二, 序列化反序列化实现对象深拷贝
- $n = serialize($m);
- $n = unserialize($n);
- $n->b = 2;
- echo $m->b;// 输出原来的 1
- echo PHP_EOL;
- // 可以看到, 普通属性实现了深拷贝, 改变普通属性 b, 不会对源对象有影响
- $n->obj->a = 3;
- echo $m->obj->a;// 输出 1, 不随新对象改变, 还是保持了原来的属性, 可以看到, 序列化和反序列化可以实现对象的深拷贝
- ?>
还有第三种方法, 其实和第二种类似, json_encode 之后再 json_decode, 实现赋值
来源: http://www.bubuko.com/infodetail-3004751.html