PHP clone 浅拷贝和深拷贝

本文介绍了PHP中的对象拷贝,包括何时需要使用拷贝以及对象复制的两种方式:浅拷贝和深拷贝。浅拷贝通过clone关键字创建新对象,但对引用类型的属性仍指向同一对象。而深拷贝则通过重写__clone()方法,确保所有属性(包括引用类型)都创建新的副本。示例展示了这两种拷贝的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.什么时候用到

在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 GTK 窗口对象,该对象持有窗口相关的资源。你可能会想复制一个新的窗口,保持所有属性与原来的窗口相同,但必须是一个新的对象(因为如果不是新的对象,那么一个窗口中的改变就会影响到另一个窗口)。还有一种情况:如果对象 A 中保存着对象 B 的引用,当你复制对象 A 时,你想其中使用的对象不再是对象 B 而是 B 的一个副本,那么你必须得到对象 A 的一个副本。

首先要明确的是:php的对象是以一个标识符来存储的,所以对对象的直接“赋值”行为相当于“传引用”

<?php
 
function dump($var){
    var_dump($var);
    echo "<br/>";
}
 
class A{
    private $a;
    protected $b;
    public $c;
 
    public function d(){
        echo "A -> d";
    }
}
 
$a1 = new A();
$a2 = $a1;
$a3 = new A();
dump($a1);
dump($a2);
dump($a3);

输出结果是:

其中可以注意到,作为对象标识符的#n,显示$a1和$a2其实是指向同一个对象,而$a3是另一个对象

所以,如果需要拷贝一个相同且全新的对象,不能直接通过=来复制,否则改变了$a1->a就相当于修改了$a2->a。

 

浅拷贝

PHP5中,类中有个魔术方法__clone(),在配合clone关键字和对象使用的时候,会自动调用(如果没有显式定义,则调用空的方法)。

clone关键字的作用是,复制某一个对象形成一个对象的“浅拷贝”,然后赋值给新的对象,此时对象标识符不同了!

<?php
 
function dump($var){
    var_dump($var);
    echo "<br/>";
}
 
class B{
    public $d;
}
 
class A{
    public $a;
    public $b;
 
    public function d(){
        echo "A -> d";
    }
}
 
$a1 = new A();
$a1->a = 123;
// 这里对象属性的值是一个对象示例,其实就是存储了对象标识符。使用clone关键字生成的拷贝中的b属性仍然指向旧对象的b属性指向的对象,这是"浅拷贝"出现的问题。如果需要指向一个新的对象,必须"深拷贝"
$a1->b = new B();
 
// PHP 5 only
$a2 = clone $a1;
 
dump($a1);
dump($a2);

输出的结果是:

可以看到,$a1和$a2明显是两个不同的对象(对象标识符不同了),但是需要留意一点是,“b”指向的对象标识符都是#2,证明两个对象都是相同的,这就是“浅拷贝”的缺陷--但有时候这两个对象确实需要相同,所以PHP的clone默认是浅拷贝

 为什么叫浅拷贝?

 因为在复制的时候,所有属性都是“值传递”的,而上面的b属性存储的是对象标识符,所以相当于做了“引用传递”,这并不是完全的拷贝,所以称为“浅拷贝”

 

深拷贝

上面讲到,使用clone关键字的时候,会自动调用旧对象的__clone()方法(然后返回拷贝的对象),所以只需要在对应的类中重写__clone()方法,使返回的对象中的“引用传递”的属性指向另一个新的对象。以下是例子(可以比较“浅拷贝”的例子,其实就多了重写__clone()的步骤):

<?php
 
function dump($var){
    var_dump($var);
    echo "<br/>";
}
 
class B{
    public $d;
}
 
class A{
    public $a;
    public $b;
 
    public function d(){
        echo "A -> d";
    }
 
    public function __clone(){
        // clone自己
        $this->b = clone $this->b;
    }
}
 
$a1 = new A();
$a1->a = 123;
// 这里对象属性的值是一个对象示例,其实就是存储了对象标识符。使用clone关键字生成的拷贝中的b属性仍然指向旧对象的b属性指向的对象,这是"浅拷贝"出现的问题。如果需要指向一个新的对象,必须"深拷贝"
$a1->b = new B();
 
// PHP 5 only
$a2 = clone $a1;
 
dump($a1);
dump($a2);

结果就不同了,注意b属性的对象标识符:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值