【问题标题】:PHP Cloning ChildrenPHP 克隆孩子
【发布时间】:2012-12-02 18:15:14
【问题描述】:

我有一个对象,它是一棵基本的树。我需要对其进行深度复制,并发现自己实现了 __clone 方法。成功的代码是:

function __clone() {
    $object = new CustomXML($this->rootElement);
    foreach ($this->elements as $key => $element) {
        $this->elements[$key] = clone $this->elements[$key];
        $object->elements[$key] = $this->elements[$key];
    }
    $object->attributes = $this->attributes;
    $object->value = $this->value;
    $object->allowHTML = $this->allowHTML;
    $object->endTag = $this->endTag;
    $object->styles = $this->styles;
    $object->childID = $this->childID;

    return $object;
}

我的问题是……我为什么要使用

$this->elements[$key] = clone $this->elements[$key];
$object->elements[$key] = $this->elements[$key];

为什么我不能直接使用

$object->elements[$key] = clone $this->elements[$key];

使用第二个仍然会留下对孩子的引用。为什么是这样? $this->elements 中的值属于同一类。

【问题讨论】:

    标签: php clone


    【解决方案1】:

    __clone() 在对象的已经创建的浅拷贝上调用。 See the documentation

    PHP 将浅拷贝所有属性并创建一个新对象而不调用其构造函数(类似于序列化和反序列化)。然后 PHP 在 new 对象上调用 __clone(),以便您可以根据自己的想法修改它。因为它修改对象,所以它不应该返回任何东西。

    你的代码(如果你总是想要一个深拷贝的话)应该是这样的:

       function __clone() {
            foreach ($this->children as $key => $child) {
                $this->children[$key] = clone $this->children[$key];
            }
       }
    

    但是我强烈建议您不要这样做!与其依赖 clone 关键字,不如添加方法以显式返回克隆对象(例如,DOMNode::cloneNode()。例如,这将让您控制您的副本应该是浅的还是深的。如果您只使用 clone 你无法控制。

    这是一个例子:

    interface DeepCopyable
    {
        /**
         * Return a copy of the current object
         *
         * @param $deep bool If TRUE, return a deep copy
         * @return object
         */
        public function copy($deep=false);
    }
    
    class TreeNode implements DeepCopyable
    {
        private $I_AM_A_CLONE = false;
        protected $children = array();
    
        function __clone() {
            $this->I_AM_A_CLONE = true;
        }
    
        public function addChild(Copyable $child) {
            $this->children[] = $child;
        }
    
        public function copy($deep=false) {
            $copy = clone $this;
            if ($deep) {
                foreach ($this->children as $n => $child) {
                    $copy->children[$n] = $child->copy($deep);
                }
            }
            return $copy;
        }
    }
    
    
    $a = new TreeNode();
    $a->addChild(new TreeNode());
    var_dump($a);
    var_dump($a->copy());
    var_dump($a->copy(true));
    

    此示例还说明了__clone()正确用法。 当克隆对象的私有或受保护属性不应与原始属性相同时,您需要添加克隆魔术方法。例如,如果您在一个对象上有一个应该是唯一的 id 属性,您可能希望确保克隆不会具有相同的 ID,并且您永远不想调用代码来控制它。或“脏”标志,或其他任何东西。

    【讨论】:

      【解决方案2】:

      经过长时间的回顾,我创建了一个测试场景,并意识到我根本不了解克隆方法。

      考虑这个示例代码:

      <?php
          class A {
              function __construct($value = "1") {
                  $this->value = $value;
                  $this->children = array();
              }
      
              function addChild() {
                  $this->children[] = new A(count($this->children));
              }
      
              function __clone() {
      
                  foreach ($this->children as $key => $child) {
                      $this->children[$key] = clone $this->children[$key];
                      //$object->children[$key] = clone $this->children[$key];
                  }
              }
          }
      
          $a = new A();
          $a->addChild();
      
          $b = clone $a;
      
          var_dump($b);
          $b->value = "test";
          $b->children[0]->value = "test2";
      
          var_dump($a);
          var_dump($b);
      

      clone 方法$b 上调用,而不是在$a 上。所以本质上,调用$this-&gt;children[$key] = clone $this-&gt;children[$key]; 会破坏引用。在这里返回一个值是没有意义的。总之,我的代码应该是这样的:

      foreach ($this->elements as $key => $element) {
          $this->elements[$key] = clone $this->elements[$key];
      }
      

      你可能会说调用$b = clone $a; 相当于做:

      $b = $a;
      $b->__clone();
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2016-07-25
        • 2017-02-07
        • 2016-10-26
        • 2014-04-26
        • 1970-01-01
        • 1970-01-01
        • 2017-01-21
        • 2017-09-16
        相关资源
        最近更新 更多