美文网首页
PHP之剑走偏锋的DeepCopy

PHP之剑走偏锋的DeepCopy

作者: ieasy_tm | 来源:发表于2017-08-19 22:19 被阅读0次

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

<?php
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信息上可以看出是cocl是独立的个体。但往往事情没有我们想像的那么简单,因为A类对象里面还可以包含B类对象的信息。看demo:

<?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;
    }
}
$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操作。

<?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;
    }
    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对象的refcount1。没问题,__clone函数能帮我们解决问题。但如果A类含有的类对象很多,那得一个个地写上clone。Golang是通过序列化和反序列化来完成这个深拷贝的,php
必须也可以:

<?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。序列化和反序列化也可以解决这个问题。欢迎大家在评论区给出批评和指正以及其他语言的深拷贝做法。

相关文章

  • PHP之剑走偏锋的DeepCopy

    php的深拷贝和浅拷贝问题,普通变量对象的赋值 = 是深拷贝,& 是浅拷贝。 输出结果: 类对象的拷贝问题就得看下...

  • GsonFormat 花样使用——App瘦身之剑走偏锋

    前言 GsonFormat相信作为安卓开发者没有几个不会用的,但是多数时候我们忽略了很多小技巧,而这些小技巧却可以...

  • 剑走偏锋

    剑走偏锋 文:曹金星 此刻是晚上九点半,设备出现异常,加热缓慢,已经开机3个小时机头温度离设定温度还差100度,等...

  • 剑走偏锋

    在我的老家柘城西关有我们县一高,在一高向东大概两公里,有一个娱乐城。 娱乐城里各种玩乐设施。 有保龄球、旱冰场、台...

  • 剑走偏锋

    太极生两仪,两仪生四象 马云是一个成功的商人,但我觉得他更像是一个哲学家。而太极就是他哲学,经商思想的根本。 太极...

  • 剑走偏锋

    9.27,周日 早起完成学习强国40分,定格吉祥分数,聊作纪念。 8:15出,尝试擦拭车内饰顶部进风口蒙尘,不料非...

  • 剑走偏锋

    记得当年看书,言陈毅元帅任外交部长时,若出国,不是带一剧团就是带一支球队,据说这既是国家的面子,也是外交手...

  • 剑走偏锋

    大路不走走小路, 正财不取赚偏财。 剑走偏锋非邪道, 不变才是堕魔障。

  • 剑走偏锋

    前几天在外面吃烤串,被他家装饰震惊了,现在的商家都有点非主流啊。 墙上画着非主流的图案,写着一些网红语句,...

  • 剑走偏锋

    那个仲夏,她想起林了。不知道为何失去了联络,那个在她心中有着绅士风度而又总是陪在她身边的男生。也许是因为去了不同的...

网友评论

      本文标题:PHP之剑走偏锋的DeepCopy

      本文链接:https://www.haomeiwen.com/subject/veqxdxtx.html