php copy 中文,PHP之剑走偏锋的DeepCopy

php的深拷贝和浅拷贝问题,普通变量对象的赋值 = 是深拷贝,& 是浅拷贝。

$a = 1;

$b = $a;

$b = 2;

$c = &$b;

$c = 3;

echo $a . PHP_EOL;

echo $b . PHP_EOL;

echo $c . PHP_EOL;

输出结果:

1

3

3

类对象的拷贝问题就得看下php版本了。php4时类对象的赋值=就是一次深拷贝。php5时类对象的赋值=就是一次浅拷贝。下面的代码是在php5.6.29的环境上运行的

// php版本是5.6.29

class CopyObj {

public $arg;

public function __construct($arg) {

$this->arg = $arg;

}

public function setArg($sa) {

$this->arg = $sa;

}

public function getArg() {

return $this->arg;

}

}

$co = new CopyObj('create obj co');

$cl = $co;

$cl->setArg('create obj cl');

echo $co->getArg() . PHP_EOL;

var_dump(xdebug_debug_zval('co'));

输出结果:

create obj cl

co: (refcount=2, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj cl' }

从打印的xdebug_debug_zval信息来看,co和cl是指向同一个zval.既然赋值=操作都是引用,那&就更不用提了,那问题来了,如何做深拷贝呢?世界上最好的语言已经帮我们考虑到了这些事情,clone()就是它。码农的世界里面简单粗暴最有效,上代码:

class CopyObj {

public $arg;

public function __construct($arg) {

$this->arg = $arg;

}

public function setArg($sa) {

$this->arg = $sa;

}

public function getArg() {

return $this->arg;

}

}

$co = new CopyObj('create obj co');

$cl = clone($co);

$cl->setArg('create obj cl');

var_dump(xdebug_debug_zval('co'));

var_dump(xdebug_debug_zval('cl'));

输出结果:

co: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj co' }

cl: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj cl' }

从zval信息上可以看出是co和cl是独立的个体。但往往事情没有我们想像的那么简单,因为A类对象里面还可以包含B类对象的信息。看demo:

class CoBi {

public $biarg = 1;

}

class CopyObj {

public $arg;

public $cobi;

public function __construct($arg) {

$this->arg = $arg;

$this->cobi = new CoBi();

}

public function setArg($sa) {

$this->arg = $sa;

}

public function getArg() {

return $this->arg;

}

}

$cj = new CoBi();

$co = new CopyObj('create obj co');

$cl = clone($co);

$cl->setArg('create obj cl');

var_dump(xdebug_debug_zval('co'));

var_dump(xdebug_debug_zval('cl'));

var_dump(xdebug_debug_zval('cj'));

输出结果:

co: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj co'; public $cobi = (refcount=2, is_ref=0)=class CoBi { public $biarg = (refcount=3, is_ref=0)=1 } }

cl: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj cl'; public $cobi = (refcount=2, is_ref=0)=class CoBi { public $biarg = (refcount=3, is_ref=0)=1 } }

cj: (refcount=1, is_ref=0)=class CoBi { public $biarg = (refcount=3, is_ref=0)=1 }

简单分析下输出的结果就知道cj的引用计数是3,所以说clone()函数只能帮你完成普通成员变量的深拷贝,但对象成员变量还是浅拷贝。这下怎么玩?难道没有办法完成深拷贝了吗?php还提供了一个__clone()函数,这个函数只有在clone对象的时候才会被调用,如果类对象不显示定义__clone,那么php直接使用默认的,复制类对象的普通成员变量。如果要做到完全的深拷贝,可以显示定义该函数,在该函数里面做一次clone操作。

class CoBi {

public $biarg = 1;

}

class CopyObj {

public $arg;

public $cobi;

public function __construct($arg) {

$this->arg = $arg;

$this->cobi = new CoBi();

}

public function setArg($sa) {

$this->arg = $sa;

}

public function getArg() {

return $this->arg;

}

public function __clone(){

$this->cobi = clone($this->cobi);

}

}

$co = new CopyObj('create obj co');

$cl = clone($co);

$cl->setArg('create obj cl');

echo $co->getArg() . PHP_EOL;

echo $cl->getArg() . PHP_EOL;

var_dump(xdebug_debug_zval('co'));

var_dump(xdebug_debug_zval('cl'));

输出结果:

co: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj co'; public $cobi = (refcount=1, is_ref=0)=class CoBi { public $biarg = (refcount=3, is_ref=0)=1 } }

cl: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj cl'; public $cobi = (refcount=1, is_ref=0)=class CoBi { public $biarg = (refcount=3, is_ref=0)=1 } }

结果看,$cobi对象的refcount是1。没问题,__clone函数能帮我们解决问题。但如果A类含有的类对象很多,那得一个个地写上clone。Golang是通过序列化和反序列化来完成这个深拷贝的,php

必须也可以:

class CoBi {

public $biarg = 1;

}

class CopyObj {

public $arg;

public $cobi;

public function __construct($arg) {

$this->arg = $arg;

$this->cobi = new CoBi();

}

public function setArg($sa) {

$this->arg = $sa;

}

public function getArg() {

return $this->arg;

}

}

$co = new CopyObj('create obj co');

$cl = serialize($co);

$cl = unserialize($cl);

$cl->setArg('create obj cl');

echo $co->getArg() . PHP_EOL;

echo $cl->getArg() . PHP_EOL;

var_dump(xdebug_debug_zval('co'));

var_dump(xdebug_debug_zval('cl'));

输出结果:

create obj co

create obj cl

co: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj co'; public $cobi = (refcount=1, is_ref=0)=class CoBi { public $biarg = (refcount=2, is_ref=0)=1 } }

cl: (refcount=1, is_ref=0)=class CopyObj { public $arg = (refcount=1, is_ref=0)='create obj cl'; public $cobi = (refcount=1, is_ref=0)=class CoBi { public $biarg = (refcount=1, is_ref=0)=1 } }

结果看,$cobi对象的refcount也是1。序列化和反序列化也可以解决这个问题。欢迎大家在评论区给出批评和指正以及其他语言的深拷贝做法。

有疑问加站长微信联系(非本文作者)

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值