なぜオブジェクトに「コピー」という概念があるのでしょうか? これは、PHP5 のオブジェクトの値の転送方法と密接に関係しています。次の簡単なコードを見てみましょう。
echo 'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
echo 'tv2 の色は: ' . $tv2->getColor();//tv2 の色は黒です
// tv2 を白にペイントします -
$tv2->setColor('白');
-
-
echo 'tv2 の色は: ' . $tv2->getColor();//tv2 の色は白です
-
エコー '
';
echo 'tv1 の色は: ' . $tv1->getColor();//tv1 の色は白です -
-
まず、tv1 と tv2 の色が両方とも黒であることがわかります。次に、tv2 の色を変更したいので、その色を白に設定します。実際に、一致しているように見えます。要件は満たしていましたが、tv1 の色を確認すると、期待したほどスムーズではありませんでした。 tv1 の色をリセットしなかったのはなぜですか?なぜなら、PHP5におけるオブジェクトの代入や値の受け渡しはすべて「参照」によって行われるからです。 PHP5 は Zend Engine II を使用しており、オブジェクトは他の一般変数のように Zval に保存されるのではなく、別の構造のオブジェクト ストアに保存されます (PHP4 では、オブジェクトは一般変数と同様に Zval に保存されます)。オブジェクトの内容 (値) ではなく、オブジェクトのポインターのみが Zval に格納されます。オブジェクトをコピーする場合、またはオブジェクトをパラメーターとして関数に渡す場合、データをコピーする必要はありません。同じオブジェクト ポインターを保持し、この特定のオブジェクトが別の zval を介してポイントしていることをオブジェクト ストアに通知するだけです。オブジェクト自体はオブジェクト ストアに配置されているため、オブジェクトに加えた変更は、そのオブジェクトへのポインタを保持するすべての zval 構造に影響します。これは、ターゲット オブジェクトに対する変更がソース オブジェクトに影響するため、プログラムに明示されています。これにより、PHP オブジェクトが常に参照によって渡されるように見えます。したがって、上記の tv2 と tv1 は実際には同じ TV インスタンスを指しており、tv1 または tv2 で行う操作は実際にはこの同じインスタンスに対するものです。したがって、「コピー」は失敗しました。このため、PHP5 ではオブジェクトのコピーに特化した操作 (clone) が提供されています。ここでオブジェクトのコピーが登場します。
クローンを使用してオブジェクトをコピーします
PHP5 のクローン言語構造を使用してオブジェクトをコピーするコードは次のとおりです:
PHPコード
-
$tv1 = 新しいテレビ ();
$tv2 = $tv1 をクローンします。
-
-
echo 'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
-
エコー '
';
echo 'tv2 の色は: ' . $tv2->getColor();//tv2 の色は黒です -
エコー '
';
-
-
//tv2 を置き換えて白にペイントします
-
$tv2->setColor('白');
-
echo 'tv2 の色は: ' . $tv2->getColor();//tv2 の色は白です -
エコー '
';
-
echo 'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
-
-
このコードの 2 行目では、clone キーワードを使用して tv1 をコピーしています。これで、tv1 と tv2 の実際のコピーが作成されました。引き続き、コピーが成功したかどうかを確認します。 tv2 の色を白に変更しましたが、tv1 の色はまだ黒であることがわかり、コピー操作は成功しました。 -
__クローンマジックメソッド
ここで、各テレビに独自の番号が必要であるという状況を考えます。この番号は、ID 番号と同様に一意である必要があるため、テレビをコピーするときに、トラブルを避けるために番号もコピーされることは望ましくありません。私たちが考え出した戦略の 1 つは、割り当てられている TV 番号をクリアし、必要に応じて番号を再割り当てすることです。
このような問題を解決するために、__clone マジック メソッドが特に使用されます。オブジェクトがコピーされる (つまり、クローン操作) ときに、__clone マジック メソッドがトリガーされます。 TV クラス Television のコードを変更し、number 属性と __clone メソッドを追加しました。コードは次のとおりです。
PHPコード
-
/**
-
*テレビカテゴリ
-
*/
-
クラス テレビ
-
{
-
-
/**
-
*テレビ番号
-
*/
-
保護された $_identity = 0;
-
-
/**
-
*画面の高さ
-
*/
-
protected $_screenLength = 300;
-
-
/**
-
*画面幅
-
*/
-
保護された $_screenHight = 200;
-
-
/**
-
※テレビ出演カラー
-
*/
-
protected $_color = '黒';
-
-
/**
-
※テレビ出演カラーに戻ります
-
*/
-
パブリック関数getColor()
-
{
-
return $this->_color;
-
}
-
-
/**
-
*テレビの外観色を設定します
-
*/
-
パブリック関数 setColor($color)
-
{
-
$this->_color = (文字列)$color;
-
$this を返します。
-
}
-
-
/**
-
* TV番号に戻る
-
*/
-
パブリック関数 getIdentity()
-
{
-
return $this->_identity;
-
}
-
-
/**
-
*テレビ番号を設定します
-
*/
-
パブリック関数 setIdentity($id)
-
{
-
$this->_identity = (int)$id;
-
$this を返します。
-
}
-
-
パブリック関数 __clone()
-
{
-
$this->setIdentity(0);
-
}
-
}
以下では、このようなテレビのオブジェクトを作成します。
PHP代
-
$tv1 = 新しいテレビ ();
$tv1->setIdentity('111111');
-
echo 'tv1 の ID は ' $tv1->getIdentity();//111111
-
エコー '
';
-
$tv2 = $tv1 をクローンします。
-
echo 'tv2 の ID は ' $tv2->getIdentity();//0
-
-
テレビ セット tv1 を作成し、その番号を 111111 に設定しました。次に、clone を使用して tv1 をコピーし、__clone マジック メソッドがトリガーされました。このメソッドは、コピーされたオブジェクト tv2 に直接作用します。メソッドでは、setIdentity メンバー メソッドを呼び出して tv2 の _identity 属性をクリアし、後で番号を付け直すことができるようにします。このことから、__clone マジック メソッドを使用すると、オブジェクトのクローンを作成するときにいくつかの追加操作を非常に便利に実行できることがわかります。
クローン操作の致命的な欠陥
クローンは本当に理想的なコピー効果を実現できるのでしょうか?場合によっては、クローン操作が想像したほど完璧ではないことがわかるはずです。上記の TV タイプを変更してテストしてみましょう。
各テレビにはリモコンが付属しているので、リモコンのクラスを開きます。リモコンとテレビは「集合」関係です (「組み合わせ」関係と比較すると、依存関係は弱いです。一般的に言えば、次のとおりです)。テレビはリモコンがなくても通常どおり使用できます)。これで、TV オブジェクトはすべてリモコン オブジェクトへの参照を保持するはずです。以下のコードを見てください
PHPコード
-
/**
-
*テレビカテゴリ
-
*/
-
クラス テレビ
-
{
-
-
/**
-
*テレビ番号
-
*/
-
保護された $_identity = 0;
-
-
/**
-
*画面の高さ
-
*/
-
protected $_screenLength = 300;
-
-
/**
-
*画面幅
-
*/
-
保護された $_screenHight = 200;
-
-
/**
-
※テレビ出演カラー
-
*/
-
protected $_color = '黒';
-
-
/**
-
* リモコンオブジェクト
-
*/
-
protected $_control = null;
-
-
/**
-
* リモコンオブジェクトをコンストラクターにロードします
-
*/
-
パブリック関数 __construct()
-
{
-
$this->setControl(new Telecontrol());
-
}
-
-
/**
-
* リモコンオブジェクトを設定します
-
*/
-
public function setControl(Telecontrol $control)
-
{
-
$this->_control = $control;
-
$this を返します。
-
}
-
-
/**
-
* リモコンオブジェクトを返します
-
*/
-
パブリック関数 getControl()
-
{
-
return $this->_control;
-
}
-
-
/**
-
※テレビ出演カラーに戻ります
-
*/
-
パブリック関数getColor()
-
{
-
return $this->_color;
-
}
-
-
/**
-
*テレビの外観色を設定します
-
*/
-
パブリック関数 setColor($color)
-
{
-
$this->_color = (文字列)$color;
-
$this を返します。
-
}
-
-
/**
-
* TV番号に戻る
-
*/
-
パブリック関数 getIdentity()
-
{
-
$ this-& gt; を返します。
}-
-
/**-
*テレビ番号を設定します -
*/ -
パブリック関数 setIdentity($id) -
{ -
$this->_identity = (int)$id; -
$これを返します-
}-
-
パブリック関数 __clone() -
{ -
$this->setIdentity(0);
-
}
-
}
-
-
-
/**
-
*リモコンカテゴリ
-
*/
-
クラステレコントロール
-
{
-
-
}
-
次に、そのような TV オブジェクトをコピーし、TV のリモコン オブジェクトを観察します。
PHPコード
$tv1 = 新しいテレビ ();
$tv2 = $tv1 をクローンします。
-
-
$contr1 = $tv1->getControl(); //tv1 のリモコン contr1 を取得します
-
$contr2 = $tv2->getControl(); //tv2 のリモコン contr2 を取得します
-
echo $tv1; //tv1 のオブジェクト ID は #1 です
-
エコー '
';
echo $contr1; // contr1 のオブジェクト ID は #2 です -
エコー '
';
-
echo $tv2; //tv2 のオブジェクト ID は #3 です
-
エコー '
';
echo $contr2; // contr2 のオブジェクト ID は #2 です -
-
-
コピー後、オブジェクト ID を確認し、クローン操作によって tv1 から tv2 をコピーします。tv1 と tv2 のオブジェクト ID はそれぞれ 1 と 3 です。これは、tv1 と tv2 が 2 つの異なる TV オブジェクトを参照していることを意味します。これは、クローンと一致しています。の操作の結果。次に、tv1 のリモコン オブジェクト contr1 と tv2 のリモコン オブジェクト contr2 をそれぞれ取得しました。それらのオブジェクト ID を確認すると、contr1 と contr2 のオブジェクト ID は両方とも 2 であり、同じものへの参照であることがわかります。オブジェクト、つまり、tv1 から tv2 をコピーしたが、リモコンはコピーされていないというのは明らかに不合理です。各テレビにはリモコンが装備されているはずであり、ここで tv2 と tv1 はリモコンを共有しています。
-
クローン操作には非常に大きな欠陥があることがわかります。クローン操作を使用してオブジェクトをコピーする場合、コピーされたオブジェクトに他のオブジェクトへの参照がある場合、参照されたオブジェクトはコピーされません。ただし、この状況は現在、「継承の再利用」に代わる「合成/集約の再利用」が主に提唱されており、「合成」と「集約」は、あるオブジェクトに別のオブジェクトへの参照を持たせることで、それを再利用します。参照されるオブジェクトのメソッド。クローンを使用する場合は、この状況を考慮する必要があります。では、オブジェクトを複製するときにこのような欠陥をどのように解決すればよいでしょうか?おそらく、前述の __clone マジック メソッドをすぐに思いついたかもしれません。これは確かに解決策です。
オプション 1: __clone マジック メソッドを使用して補います
__clone マジック メソッドの使用についてはすでに紹介しました。コピーされたオブジェクト内の他のオブジェクトの参照を、__clone メソッド内の新しいオブジェクトに再参照できます。変更された __clone() マジック メソッドを見てみましょう:
PHPコード
-
パブリック関数 __clone()
-
{
-
$this->setIdentity(0);
// リモート コントロール オブジェクトをリセットします -
$this->setControl(new Telecontrol());
-
}
-
04 行目では、コピーした TV オブジェクトのリモコンをリセットします。前の方法に従ってオブジェクト ID を確認すると、2 つの TV のリモコンのオブジェクト ID が異なることがわかり、問題は解決されました。
しかし、この方法はおそらくあまり良くありません。コピーされたオブジェクトに他のオブジェクトへの参照が複数ある場合、__clone メソッドでそれらを 1 つずつリセットする必要があります。さらに悪いことに、コピーされたオブジェクトのクラスが によって提供されている場合です。ただし、コードを変更することはできないため、コピー操作は基本的にスムーズに完了しません。
オブジェクトをコピーするにはクローンを使用します。この種のコピーは「浅いコピー」と呼ばれます。コピーされたオブジェクトのすべての変数には元のオブジェクトと同じ値が含まれており、他のオブジェクトへの参照はすべて元のオブジェクトを指し続けます。つまり、浅いコピーは、参照しているオブジェクトではなく、問題のオブジェクトのみをコピーします。 「浅いコピー」と比較すると、もちろん「深いコピー」もあります。コピーされたオブジェクトのすべての変数には、他のオブジェクトを参照する変数を除き、元のオブジェクトと同じ値が含まれます。つまり、ディープ コピーでは、コピー対象のオブジェクトが参照するすべてのオブジェクトがコピーされます。ディープ コピーでは、何層まで進むかを決定する必要がありますが、これは簡単に決定できない問題であり、さらに、循環参照の問題が発生する可能性があるため、慎重に処理する必要があります。オプション 2 はディープ コピー ソリューションです。
解決策 2: ディープコピーにシリアル化を使用する
PHP にはシリアル化 (シリアル化) 関数と逆シリアル化 (アンシリアル化) 関数があり、serialize() を使用してオブジェクトをストリームに書き込み、ストリームからオブジェクトを読み取るだけで、オブジェクトがコピーされます。 JAVA 言語では、このプロセスは「冷却」および「解凍」と呼ばれます。以下でこのメソッドをテストします:
PHPコード
$tv1 = 新しいテレビ ();
$tv2 = unserialize(serialize($tv1));//シリアル化してから逆シリアル化します -
-
$contr1 = $tv1->getControl(); //tv1 のリモコン contr1 を取得します -
$contr2 = $tv2->getControl(); //tv2 のリモコン contr2 を取得します -
-
echo $tv1; //tv1 のオブジェクト ID は #1 です -
エコー '
';
-
echo $contr1; // contr1 のオブジェクト ID は #2 です
-
エコー '
';
echo $tv2; //tv2 のオブジェクト ID は #4 です -
エコー '
';
-
echo $contr2; // contr2 のオブジェクト ID は #5 です
-
-
出力を見ると、tv1 と tv2 に異なるリモコンがあることがわかります。これは、オプション 1 よりもはるかに便利です。シリアル化は再帰的なプロセスです。オブジェクト内で参照されるオブジェクトの数や、オブジェクトを完全にコピーできるレイヤーの数を気にする必要はありません。このソリューションを使用する場合、__clone マジック メソッドをトリガーして追加の操作を完了することはできないことに注意してください。 もちろん、ディープ コピー後に再度クローン操作を実行して __clone マジック メソッドをトリガーすることはできますが、効率には多少の影響があります。さらに、この解決策はコピーされたオブジェクトとすべての参照オブジェクトの __sleep および __wakeup マジック メソッドをトリガーするため、これらの状況を考慮する必要があります。 -
まとめ
オブジェクトのコピー方法が異なれば効果も異なります。どの方法を使用するか、特定のアプリケーション要件に基づいてコピー方法を改善する方法を検討する必要があります。 PHP5 のオブジェクト指向機能は比較的 JAVA に近いので、JAVA から多くの貴重な経験を学ぶことができると思います
。
http://www.bkjia.com/PHPjc/478553.htmlwww.bkjia.comtruehttp://www.bkjia.com/PHPjc/478553.html技術記事この記事では、PHP5 のオブジェクト レプリケーション技術について、簡単な記事から詳細なオリジナル記事まで説明します。エラーや不適切な点がありますので、オブジェクト レプリケーションの起源とオブジェクトが重複する理由を指摘できれば幸いです。 .