• 【PHP】对象的复制(拷贝)与__clone()方法


    参考链接:

    1、php.net官网文档 - 对象复制

    什么时候用到?摘自php.net:

    在多数情况下,我们并不需要完全复制一个对象来获得其中属性。但有一个情况下确实需要:如果你有一个 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);
    

    输出的结果是:

    object(A)#1 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 
    object(A)#1 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 
    object(A)#2 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 
    

    其中可以注意到,作为对象标识符的#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);
    

    输出的结果是:

    object(A)#1 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 
    object(A)#3 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 
    

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

    为什么叫浅拷贝(shallow copy)?

    因为在复制的时候,所有的属性都是“值传递”的,而上面的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属性的对象标识符:

    object(A)#1 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 
    object(A)#3 (2) { ["a"]=> int(123) ["b"]=> object(B)#4 (1) { ["d"]=> NULL } } 
    
  • 相关阅读:
    DataGridView 移动行
    DataGridView 显示提示信息
    模块和类的区别
    C# 流总结
    很口语I'm on my way
    读书笔记_Effective_C++_条款二十三:宁以nonmember、nonfriend替换member函数
    PDB文件:每个开发人员都必须知道的
    qq农场,数据抓包分析,实现源码,图片讲解
    连连看外挂消去算法分析
    关于“服务器提交了协议冲突. Section=ResponseStatusLine"问题请
  • 原文地址:https://www.cnblogs.com/tyrus/p/php_object_clone.html
Copyright © 2020-2023  润新知