浅复制与深复制
-
浅复制:变量复制时,只复制变量的引用到新变量,复制前后的变量指向相同的变量地址,改变旧变量的值会同时影响复制后的新变量;
-
深复制:变量复制过程为完整的复制,复制后的值与旧变量完全独立,改变旧变量值不影响新变量。
PHP 对于普通变量的 “=” 复制全部都是深复制,对于对象的复制,根据 PHP 版本有所有区别,在 PHP 5 之前的版本中,对象间的赋值是深复制,PHP 5 之后的版本中,赋值操作产生的对象复制是浅复制:
$a = 1;
$b = $a;
$a = 2;
var_dump($a, $b); // $a = 2, $b = 1
class Foo
{
public $bar = 1;
}
$obj1 = new Foo();
$obj2 = $obj1;
$obj1->bar++;
var_dump($obj1->bar, $obj2->bar); // 结果都为2
此外当对象作为函数参数传递时候,也是引用传递,且无论是否有 &
标识,都是如此。
clone 关键字
不难想到,引用复制相对值复制的优势在于复制速度快,节省内存空间,但在实际编程中,确实有不少情况是需要使用值复制获取新对象的,这时候,就可以使用 PHP 中的 clone
关键字:
class Foo
{
public $bar = 1;
}
$obj1 = new Foo();
$obj2 = clone $obj1;
$obj1->bar++;
var_dump($obj1->bar, $obj2->bar); // 结果:$obj1->bar = 2,$ob2->bar = 1
上面的例子中,使用 clone 关键字复制得到的新对象属性没有和旧对象关联,似乎是实现了深复制过程,再看下面的例子:
class Test
{
public $option = 1;
}
class Foo
{
public $bar = 1;
public $test = new test();
}
$obj1 = new Foo();
$obj2 = clone $obj1;
$obj1->bar++;
$obj1->test->option++;
var_dump($obj1->bar, $obj2->bar, $obj1->test->option, $obj2->test->option); // 结果:$obj1->bar = 2,$ob2->bar = 1,$obj1->test->option = 2,$obj2->test->option = 2
可以发现,使用 clone 关键字拷贝对象时,原对象中的普通属性会按照值复制的方式拷贝到新对象中,而对于对象属性,则仍旧是引用复制的形式进行拷贝的。
在实际项目中,如果 clone 后的对象属性出现例子中的情况,有可能会和复制对象的初衷违背,产生意料之外的 bug,为了避免这种情况,可以使用 __clone
拦截器(魔术方法):
class Test
{
public $option = 1;
}
class Foo
{
public $bar = 1;
public $test = new test();
// __clone 方法会在使用 clone 时候自动执行
public function __clone()
{
$this->test = clone new test();
}
}