2

やや基本的なツリーであるオブジェクトがあります。私はそれの深いコピーを作成する必要があり、__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];

2番目のものを使用しても、子への参照が残ります。どうしてこれなの?$this->elementsの値は同じクラスです。

4

2 に答える 2

2

__clone()オブジェクトの作成済みの浅いコピーで呼び出されます。ドキュメントを参照してください

PHPは、すべてのプロパティを浅くコピーし、コンストラクターを呼び出さずに新しいオブジェクトを作成します(シリアル化および逆シリアル化と同様)。次に、PHPは新しいオブジェクトを呼び出し__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を持たないようにし、呼び出しコードでそれを制御したくない場合があります。または「ダーティ」フラグなど。

于 2012-12-14T19:13:54.760 に答える
1

これを長い間検討した結果、テストシナリオを作成し、クローンの方法をまったく理解していないことに気付きました。

このサンプルコードを考えてみましょう。

<?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);

クローンメソッドは、$ aではなく、で呼び出されています。$bつまり、本質的に、呼び出し$this->children[$key] = clone $this->children[$key];は参照を壊しています。ここで値を返すことは無意味です。要約すると、私のコードは次のようになっているはずです。

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

$b = clone $a;呼び出すことは、次のことを行うことと同等であると言うかもしれません。

$b = $a;
$b->__clone();
于 2012-12-14T18:37:44.920 に答える