ホームページ バックエンド開発 PHPチュートリアル php5 オブジェクトのコピー、クローン、シャロー コピー、ディープ コピー

php5 オブジェクトのコピー、クローン、シャロー コピー、ディープ コピー

Jul 25, 2016 am 08:42 AM

オブジェクトコピーの起源
オブジェクトに「コピー」という概念があるのはなぜですか? これは、PHP5 のオブジェクトの値転送メソッドと密接に関係しています。次の簡単なコードを見てみましょう

phpコード

* /**
* * テレビ
**/
*クラステレビ
* {
* /**
* * 画面の高さ
**/
* 保護されています
$_screenLength = 300;
*
* /**
* * 画面幅
**/
* 保護されています
$_screenHight = 200; *
* /**
* * TV出演カラー
**/
* 保護されています
$_color = '黒';
*
* /**
* * テレビ出演カラーに戻る
**/
*公開
関数getColor()
* {
*戻る
$this->_color
* }
*
* /**
* * テレビの表示色を設定します
**/
*公開
関数setColor($color)
* {
* $this->_color = (文字列)$color
*戻る
$これ
* }
* }
*
* $tv1 = 新しいテレビ ();
* $tv2 = $tv1;
このコードはテレビクラス Television を定義しており、$tv1 はテレビのインスタンスであり、通常の変数代入方法に従って $tv1 の値を $t2 に代入します。これで、$tv1 と $tv2 という 2 つのテレビができました。これは本当に事実でしょうか?試してみましょう。


PHPコード

*エコー
'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
*エコー

*エコー

'tv2 の色は: ' . $tv2->getColor();//tv2 の色は黒です

*エコー

*
* //tv2 を白にペイントします

* $tv2->setColor('white'); *
*エコー
'tv2 の色は: ' . $tv2->getColor();//tv2 の色は白です
*エコー

*エコー
'tv1 の色は: ' . $tv1->getColor();//tv1 の色は白です


まず、tv1 と tv2 の色が両方とも黒であることがわかります。今度は tv2 の色を変更したいので、その色を白に設定します。実際に白になっているように見えます。しかし、tv1 の色を確認すると、tv1 も黒から白に変わっていることがわかりました。 tv1 の色をリセットしなかったのはなぜですか?なぜなら、PHP5におけるオブジェクトの代入や値の受け渡しはすべて「参照」によって行われるからです。 PHP5 は Zend Engine II を使用しており、オブジェクトは他の一般変数のように Zval に保存されるのではなく、別の構造のオブジェクト ストアに保存されます (PHP4 では、オブジェクトは一般変数と同様に Zval に保存されます)。コンテンツ (値) ではなく、オブジェクトへのポインターのみが Zval に格納されます。オブジェクトをコピーする場合、またはオブジェクトをパラメーターとして関数に渡す場合、データをコピーする必要はありません。同じオブジェクト ポインターを保持し、この特定のオブジェクトが別の zval を介してポイントしていることをオブジェクト ストアに通知するだけです。オブジェクト自体はオブジェクト ストアに配置されているため、オブジェクトに加えた変更は、そのオブジェクトへのポインタを保持するすべての zval 構造に影響します。これは、ターゲット オブジェクトに対する変更がソース オブジェクトに影響するため、プログラムに明示されています。これにより、PHP オブジェクトが常に参照によって渡されるように見えます。したがって、上記の tv2 と tv1 は実際には同じ TV インスタンスを指しており、tv1 または tv2 で行う操作は実際にはこの同じインスタンスに対するものです。したがって、「コピー」は失敗しました。このため、PHP5 ではオブジェクトのコピーに特化した操作 (clone) が提供されています。ここでオブジェクトのコピーが登場します。


クローンを使用してオブジェクトをコピーする
PHP5 のクローン言語構造を使用してオブジェクトをコピーします。コードは次のとおりです:
[size=+0]PHP コード

* [size=+0]$tv1 = 新しいテレビ();
* $tv2 = $tv1 のクローン ;
*

*エコー
'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です
*エコー

*エコー
'tv2 の色は: ' . $tv2->getColor();//tv2 の色は黒です
*エコー


*

* //tv2 を交換して白にペイントします

* $tv2->setColor('white');
*
*エコー

'tv2 の色は: ' . $tv2->getColor();//tv2 の色は白です
*エコー

*エコー
'tv1 の色は: ' . $tv1->getColor();//tv1 の色は黒です

このコードの 2 行目では、clone キーワードを使用して tv1 をコピーします。これで、tv1、tv2 の実際のコピーが得られます。コピーが成功したかどうかを確認するために、引き続き前の方法に従います。 tv2 の色を白に変更しましたが、tv1 の色はまだ黒であることがわかり、コピー操作は成功しました。


__クローンマジックメソッド



ここで、各テレビに独自の番号が必要であるという状況を考えます。この番号は、ID 番号と同様に一意である必要があるため、テレビをコピーするときに、問題が発生するのを避けるためにこの番号もコピーされることは望ましくありません。私たちが考え出した戦略の 1 つは、割り当てられている TV 番号をクリアし、必要に応じて番号を再割り当てすることです。
このような問題を解決するために、__clone マジック メソッドが特に使用されます。オブジェクトがコピーされる (つまり、クローン操作) ときに、__clone マジック メソッドがトリガーされます。 TV クラス Television のコードを変更し、number 属性と __clone メソッドを追加しました。コードは次のとおりです。
PHP代

* /**
* * テレビ
**/
*クラステレビ
* {
*
* /**
* * テレビ番号
**/
* 保護されています
$_identity = 0;
*
* /**
* * 画面の高さ
**/
* 保護されています
$_screenLength = 300;
*
* /**
* * 画面幅
**/
* 保護されています
$_screenHight = 200;
*
* /**
* * TV出演カラー
**/
* 保護されています
$_color = '黒';
*
* /**
* * テレビ出演カラーに戻ります
**/
*公開
関数getColor()
* {
*戻る
$this->_color;
* }
*
* /**
* * テレビの表示色を設定します
**/
*公開
関数setColor($color)
* {
* $this->_color = (文字列)$color;
*戻る
$これ;
* }
*
* /**
* * テレビ番号を返します
**/
*公開
関数getIdentity()
* {
*戻る
$this->_identity;
* }
*
* /**
* * テレビ番号を設定します
**/
*公開
関数 setIdentity($id)
* {
* $this->_identity = (int)$id;
*戻る
$これ;
* }
*
*公開
関数__clone()
* {
* $this->setIdentity(0);
* }
* }


次に、このような電子ビデオのオブジェクトを作成します。

PHP代

* $tv1 = 新しいテレビ();
* $tv1->setIdentity('111111');
*エコー
'tv1 の ID は ' です。 $tv1->getIdentity();//111111
*エコー
」;

*
* $tv2 = $tv1 のクローン;
*エコー
'tv2 の ID は ' です。 $tv2->getIdentity();//0


私は一台のテレビテレビ tv1 を作成し、その番号を 111111 に設定しました。その後、クローンを使用して tv1 を作成し、tv2 を取得しました。このとき、__clone 魔方法が起動され、この方法は、取得したオブジェクト tv2 に直接作用します。私は、__clone メソッドで setIdentity メソッドを使用して、tv2 の _identity プロパティをクリアし、後で新しい番号を実行できるようにしました。いくつかの追加の操作が行われました。
クローン操作の危険な缺陷
クローンの真の能力は理想的な複製効果に達していますか? 場合によっては、この問題が発生し、クローン操作が私が考えているような完璧なものではありません。
每台電影机都市附带一個のリモートコントローラー、所以我们将会有一個のリモートコントローラー類、リモートコントローラーと電子ビデオは一種の「聚合」関係関係です一般に、リモコンのない電子機器でも通常使用できるため、私の電子機器オブジェクトには、リモコン オブジェクトへの参照が含まれています。以下のコードを見てください
PHPコード

* /**
* * テレビ
**/
*クラステレビ
* {
*
* /**
* * テレビ番号
**/
* 保護されています
$_identity = 0;
*
* /**
* * 画面の高さ
**/
* 保護されています
$_screenLength = 300; *
* /**
* * 画面幅
**/
* 保護されています
$_screenHight = 200;
*
* /**
* * TV出演カラー
**/
* 保護されています
$_color = '黒';
*
* /**
* * リモコンオブジェクト
**/
* 保護されています
$_control = null; *
* /**
* * コンストラクターにリモコンオブジェクトをロードします
**/
*公開
関数__construct()
* {
* $this->setControl(new Telecontrol());
* }
*
* /**
* * リモコンオブジェクトを設定します
**/
*公開
関数 setControl(Telecontrol $control)
* {
* $this->_control = $control
*戻る
$これ
* }
*
* /**
* * リモコンオブジェクトを返します
**/
*公開
関数getControl()
* {
*戻る
$this->_control
* }
*
* /**
* * テレビ出演カラーに戻ります
**/
*公開
関数getColor()
* {
*戻る
$this->_color
* }
*
* /**
* * テレビの表示色を設定します
**/
*公開
関数setColor($color)
* {
* $this->_color = (文字列)$color
*戻る
$これ
* }
*
* /**
* * テレビ番号を返します
**/
*公開
関数getIdentity()
* {
*戻る
$this->_identity
* }
*
* /**
* * テレビ番号を設定します
**/
*公開
関数 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 を取得します
*エコー
$tv1; //tv1 のオブジェクト ID は #1 です
*エコー

*エコー
$contr1; // contr1 のオブジェクト ID は #2 です
*エコー


*エコー

$tv2; //tv2 のオブジェクト ID は #3 です
*エコー

*エコー

$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() マジック メソッドを見てみましょう:
[size=+0][size=+0]PHP コード

* [size=+0][size=+0]公開

関数__clone()
* {
* $this->setIdentity(0); * //リモコンオブジェクトをリセットします
* $this->setControl(new Telecontrol());
* }

04 行目では、コピーした TV オブジェクトのリモコンをリセットします。前の方法に従ってオブジェクト ID を確認すると、2 台の TV のリモコンのオブジェクト ID が異なることがわかり、問題は解決されました。

しかし、このメソッドはおそらくあまり良くありません。コピーされたオブジェクトに他のオブジェクトへの参照が複数ある場合、__clone メソッドでそれらを 1 つずつリセットする必要があります。サードパーティではコードを変更できないため、基本的にコピー操作はスムーズに完了しません。
オブジェクトのコピーにはクローンを使用します。この種のコピーは「浅いコピー」と呼ばれます。コピーされたオブジェクトのすべての変数には元のオブジェクトと同じ値が含まれており、他のオブジェクトへの参照はすべて元のオブジェクトを指し続けます。つまり、浅いコピーは、参照しているオブジェクトではなく、問題のオブジェクトのみをコピーします。 「浅いコピー」と比較すると、もちろん「深いコピー」もあります。コピーされたオブジェクトのすべての変数には、他のオブジェクトを参照する変数を除き、元のオブジェクトと同じ値が含まれます。つまり、ディープ コピーでは、コピー対象のオブジェクトが参照するすべてのオブジェクトがコピーされます。ディープ コピーでは、何層まで進むかを決定する必要がありますが、これは簡単に決定できない問題であり、さらに、循環参照の問題が発生する可能性があるため、慎重に処理する必要があります。オプション 2 はディープ コピー ソリューションです。

オプション 2: ディープ コピーにシリアル化を使用する
PHP にはシリアライズ関数とアンシリアライズ関数があります。serialize() を使用してオブジェクトをストリームに書き込み、ストリームからオブジェクトを読み取るだけで、オブジェクトがコピーされます。 Java 言語では、このプロセスは「冷却」および「解凍」と呼ばれます。以下でこのメソッドをテストします:
[size=+0][size=+0]PHP コード

* [size=+0][size=+0]$tv1 = 新しいテレビ ();
* $tv2 = unserialize(serialize($tv1));//シリアル化してから逆シリアル化する
*

* $contr1 = $tv1->getControl() //tv1 のリモコン contr1 を取得します
* $contr2 = $tv2->getControl() //tv2 のリモコン contr2 を取得します
*

*エコー
$tv1; //tv1 のオブジェクト ID は #1 です
*エコー

*エコー
$contr1; // contr1 のオブジェクト ID は #2 です
*エコー


*エコー

$tv2; //tv2 のオブジェクト ID は #4 です
*エコー

*エコー

$contr2; // contr2 のオブジェクト ID は #5 です
出力を見ると、tv1とtv2には異なるリモコンが付いています。これは、オプション 1 よりもはるかに便利です。シリアル化は再帰的なプロセスです。オブジェクト内で参照されるオブジェクトの数や、オブジェクトを完全にコピーできるレイヤーの数を気にする必要はありません。このソリューションを使用する場合、__clone マジック メソッドをトリガーして追加の操作を完了することはできないことに注意してください。 もちろん、ディープ コピー後に再度クローン操作を実行して __clone マジック メソッドをトリガーすることはできますが、効率には多少の影響があります。さらに、この解決策はコピーされたオブジェクトとすべての参照オブジェクトの __sleep および __wakeup マジック メソッドをトリガーするため、これらの状況を考慮する必要があります。



概要


オブジェクトのコピー方法が異なれば効果も異なります。どの方法を使用するか、特定のアプリケーション要件に基づいてコピー方法を改善する方法を検討する必要があります。 PHP5 のオブジェクト指向機能は比較的 JAVA に近いため、JAVA から多くの貴重な経験を学ぶことができると思います。
クローン
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。

ホットAIツール

Undresser.AI Undress

Undresser.AI Undress

リアルなヌード写真を作成する AI 搭載アプリ

AI Clothes Remover

AI Clothes Remover

写真から衣服を削除するオンライン AI ツール。

Undress AI Tool

Undress AI Tool

脱衣画像を無料で

Clothoff.io

Clothoff.io

AI衣類リムーバー

AI Hentai Generator

AI Hentai Generator

AIヘンタイを無料で生成します。

ホットツール

メモ帳++7.3.1

メモ帳++7.3.1

使いやすく無料のコードエディター

SublimeText3 中国語版

SublimeText3 中国語版

中国語版、とても使いやすい

ゼンドスタジオ 13.0.1

ゼンドスタジオ 13.0.1

強力な PHP 統合開発環境

ドリームウィーバー CS6

ドリームウィーバー CS6

ビジュアル Web 開発ツール

SublimeText3 Mac版

SublimeText3 Mac版

神レベルのコード編集ソフト(SublimeText3)

JSON Web Tokens(JWT)とPHP APIでのユースケースを説明してください。 JSON Web Tokens(JWT)とPHP APIでのユースケースを説明してください。 Apr 05, 2025 am 12:04 AM

JWTは、JSONに基づくオープン標準であり、主にアイデンティティ認証と情報交換のために、当事者間で情報を安全に送信するために使用されます。 1。JWTは、ヘッダー、ペイロード、署名の3つの部分で構成されています。 2。JWTの実用的な原則には、JWTの生成、JWTの検証、ペイロードの解析という3つのステップが含まれます。 3. PHPでの認証にJWTを使用する場合、JWTを生成および検証でき、ユーザーの役割と許可情報を高度な使用に含めることができます。 4.一般的なエラーには、署名検証障害、トークンの有効期限、およびペイロードが大きくなります。デバッグスキルには、デバッグツールの使用とロギングが含まれます。 5.パフォーマンスの最適化とベストプラクティスには、適切な署名アルゴリズムの使用、有効期間を合理的に設定することが含まれます。

PHPにおける後期静的結合の概念を説明します。 PHPにおける後期静的結合の概念を説明します。 Mar 21, 2025 pm 01:33 PM

記事では、PHP 5.3で導入されたPHPの後期静的結合(LSB)について説明し、より柔軟な継承を求める静的メソッドコールのランタイム解像度を可能にします。 LSBの実用的なアプリケーションと潜在的なパフォーマ

フレームワークセキュリティ機能:脆弱性から保護します。 フレームワークセキュリティ機能:脆弱性から保護します。 Mar 28, 2025 pm 05:11 PM

記事では、入力検証、認証、定期的な更新など、脆弱性から保護するためのフレームワークの重要なセキュリティ機能について説明します。

フレームワークのカスタマイズ/拡張:カスタム機能を追加する方法。 フレームワークのカスタマイズ/拡張:カスタム機能を追加する方法。 Mar 28, 2025 pm 05:12 PM

この記事では、フレームワークにカスタム機能を追加し、アーキテクチャの理解、拡張ポイントの識別、統合とデバッグのベストプラクティスに焦点を当てています。

PHPのCurlライブラリを使用してJSONデータを含むPOSTリクエストを送信する方法は? PHPのCurlライブラリを使用してJSONデータを含むPOSTリクエストを送信する方法は? Apr 01, 2025 pm 03:12 PM

PHP開発でPHPのCurlライブラリを使用してJSONデータを送信すると、外部APIと対話する必要があることがよくあります。一般的な方法の1つは、Curlライブラリを使用して投稿を送信することです。

確固たる原則と、それらがPHP開発にどのように適用されるかを説明してください。 確固たる原則と、それらがPHP開発にどのように適用されるかを説明してください。 Apr 03, 2025 am 12:04 AM

PHP開発における固体原理の適用には、次のものが含まれます。1。単一責任原則(SRP):各クラスは1つの機能のみを担当します。 2。オープンおよびクローズ原理(OCP):変更は、変更ではなく拡張によって達成されます。 3。Lischの代替原則(LSP):サブクラスは、プログラムの精度に影響を与えることなく、基本クラスを置き換えることができます。 4。インターフェイス分離原理(ISP):依存関係や未使用の方法を避けるために、細粒インターフェイスを使用します。 5。依存関係の反転原理(DIP):高レベルのモジュールと低レベルのモジュールは抽象化に依存し、依存関係噴射を通じて実装されます。

システムの再起動後にUnixSocketの権限を自動的に設定する方法は? システムの再起動後にUnixSocketの権限を自動的に設定する方法は? Mar 31, 2025 pm 11:54 PM

システムが再起動した後、UnixSocketの権限を自動的に設定する方法。システムが再起動するたびに、UnixSocketの許可を変更するために次のコマンドを実行する必要があります:sudo ...

See all articles