在PHP5中,对象间的赋值被默认视为地址引用,例如:

<?php

$objB = $objA;

?>

针对上面这句代码,我们看一下内存分配结构:

从上面示意图中我们可以看出,定义$objA对象时,系统会为其分配一块内存(①箭头),当$objB被赋值$objA时,系统没有再分配内存,而是将$objB的引用直接指向了$objA内存,实际上这两个对象指向的是同一块内存,这时,当我们修改$objB的值时,$objA的值也跟着变化了,这就使得如果我们需要两个同样的、相对独立的对象就需要分别定义,操作麻烦,而且程序的可读行定也不好。为了解决这个问题,PHP为我们提供了一种可以克隆对象的方法clone,在PHP手册里对clone方法介绍的比较简单,下面UncleToo用实例为大家介绍一下clone方法的使用。

示例一:

<?php

class subclass{

private $name;

private $age;

public function __construct(){

$this->name = ‘张三’;

$this->age = ’30’;

}

public function __clone(){

$this->name = ‘李四’;

$this->age = ’29’;

}

}

class myclass{

public $p1;

public $p2;

public function __construct(){

}

public function __clone(){

$this->p1 = clone $this->p1;

}

}

$obj = new myclass();

$obj->p1 = new subclass();

$obj->p2 = new subclass();

$obj2 = clone $obj;

echo ‘<pre>’;

echo ‘这是obj<br>’;

var_dump($obj);

echo ‘———————–‘;

echo ‘<br>这是obj2<br>’;

var_dump($obj2);

echo ‘</pre>’;

?>

输出:

这是objobject(myclass)#1 (2) {

[“p1”]=>

object(subclass)#2 (2) {

[“name”:”subclass”:private]=>

string(4) “张三”

[“age”:”subclass”:private]=>

string(2) “30”

}

[“p2”]=>

object(subclass)#3 (2) {

[“name”:”subclass”:private]=>

string(4) “张三”

[“age”:”subclass”:private]=>

string(2) “30”

}

}

———————–这是obj2object(myclass)#4 (2) {

[“p1”]=>

object(subclass)#5 (2) {

[“name”:”subclass”:private]=>

string(4) “李四”

[“age”:”subclass”:private]=>

string(2) “29”

}

[“p2”]=>

object(subclass)#3 (2) {

[“name”:”subclass”:private]=>

string(4) “张三”

[“age”:”subclass”:private]=>

string(2) “30”

}

}

可以看到,$obj2 clone $obj,$obj->p1 clone $obj->p1,执行了 __clone()方法。而在__clone方法中,对p1的name与 age属性进行修改,因此p1的name与age 发生变化。而p2因为没有执行clone()方法,所以新复制出来的$obj2->p2的属性与 $obj->p2一样。

示例二:

<?php

class myclass{

public $num = null;

public $msg = null;

public function __construct(){

$this->num = & $this->num;

$this->msg = ‘OK’;

}

public function __clone(){

$this->num = 2;

}

}

$obj = new myclass();

$obj->num = 1;

echo ‘print obj values<br>’;

var_dump($obj);

echo ‘<br><br>’;

$obj2 = clone $obj;

echo ‘clone obj to obj2<br>’;

echo ‘obj->num:’.$obj->num.'<br>’;

echo ‘obj->msg:’.$obj->msg.'<br>’;

echo ‘obj2->num:’.$obj2->num.'<br>’;

echo ‘obj2->msg:’.$obj2->msg.'<br><br>’;

$obj2->num = 3;

$obj2->msg = ‘Yes’;

echo ‘set obj2->num=3, obj2->msg=Yes<br>’;

echo ‘obj->num:’.$obj->num.'<br>’;

echo ‘obj->msg:’.$obj->msg.'<br>’;

echo ‘obj2->num:’.$obj2->num.'<br>’;

echo ‘obj2->msg:’.$obj2->msg.'<br><br>’;

?>

输出:

print obj values

object(myclass)#1 (2) { [“num”]=> &int(1) [“msg”]=> string(2) “OK” }

clone obj to obj2

obj->num:2

obj->msg:OK

obj2->num:2

obj2->msg:OK

set obj2->num=3, obj2->msg=Yes

obj->num:3

obj->msg:OK

obj2->num:3

obj2->msg:Yes

在这个例子中,因$this->num = &$this->num,因此clone()之后,新对象的$this->num都是引用旧对象的内存地址。因此旧对象这个属性发生变化,新对象这个属性也会发生变化,实现了某些属性的引用保持不变。而$this->msg并不是地址引用,因此,新对象的msg发生变化,不会影响到旧对象。

注意:$this->num = & $this->num 使用对象属性地址引用时,在clone之前不能echo/print这个属性,否则地址引用会失效。

上例中,如果在$obj2 = clone $obj前加上,echo $obj->num; 则会使地址引用实效,即$obj2->num发生改变,$obj->num并不会发生改变。