亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 語言 > PHP > 正文

php5對象復制、clone、淺復制與深復制實例詳解

2024-05-05 00:10:55
字體:
來源:轉載
供稿:網友

本文實例講述了php5對象復制、clone、淺復制與深復制。分享給大家供大家參考,具體如下:

對象復制的由來

為什么對象會有“復制”這個概念,這與PHP5中對象的傳值方式是密切相關的,讓我們看看下面這段簡單的代碼

PHP代碼

/*** 電視機類*/class Television{  /**   屏幕高度   */  protected $_screenLength = 300;  /**   屏幕寬度   */  protected $_screenHight = 200;  /**   電視機外觀顏色   */  protected $_color    = 'black';  /**   返回電視外觀顏色   */  public function getColor()  {    return $this->_color;  }  /**   設置電視機外觀顏色   */  public function setColor($color)  {    $this->_color = (string)$color;    return $this;  }}$tv1 = new Television();$tv2 = $tv1;

這段代碼定義了一個電視機的類 Television , $tv1為一個電視機的實例,然后我們按照普通的變量賦值方式將$tv1的值賦給$t2。那么現在我們擁有兩臺電視機$tv1和$tv2了,真的是這樣的嗎?我們來測試一下。

PHP代碼

echo 'color of tv1 is: ' . $tv1->getColor();//tv1的顏色是blackecho '<br>';echo 'color of tv2 is: ' . $tv2->getColor();//tv2的顏色是blackecho '<br>';//把tv2涂成白色$tv2->setColor('white');echo 'color of tv2 is: ' . $tv2->getColor();//tv2的顏色是whiteecho '<br>';echo 'color of tv1 is: ' . $tv1->getColor();//tv1的顏色是white

首先我們看到tv1和tv2的顏色都是black,現在我們希望tv2換個顏色,所以我們將它的顏色設置成了white,我們再看看tv2的顏色,確實成為了white,似乎滿足了我們的要求,可是并沒有想象中的那么順利,當我們接著看tv1的顏色的時候,我們發現tv1也由black邊成了 white。我們并沒有重新設置tv1的顏色,為什么tv1會重black變成white呢?這是因為PHP5中對象的賦值和傳值都是以“引用”的方式。 PHP5 使用了Zend引擎II,對象被儲存于獨立的結構Object Store中,而不像其它一般變量那樣儲存于Zval中(在PHP4中對象和一般變量一樣存儲于Zval)。在Zval中僅存儲對象的指針而不是內容 (value)。當我們復制一個對象或者將一個對象當作參數傳遞給一個函數時,我們不需要復制數據。僅僅保持相同的對象指針并由另一個zval通知現在這個特定的對象指向的Object Store。由于對象本身位于Object Store,我們對它所作的任何改變將影響到所有持有該對象指針的zval結構----表現在程序中就是目標對象的任何改變都會影響到源對象。.這使 PHP對象看起來就像總是通過引用(reference)來傳遞。所以以上的tv2和tv1其實是指向同一個電視機實例,我們對tv1或則tv2所做的操作其實都是針對這同一個實例。因此我們的“復制”失敗了。看來直接變量賦值的方式并不能拷貝對象,為此 PHP5提供了一個專門用于復制對象的操作,也就是 clone 。這就是對象復制的由來。

用clone(克?。﹣韽椭茖ο?/strong>

我們現在使用PHP5的clone語言結構來復制對象,代碼如下:

PHP代碼

$tv1 = new Television();$tv2 = clone $tv1;echo 'color of tv1 is: ' . $tv1->getColor();//tv1的顏色是blackecho '<br>';echo 'color of tv2 is: ' . $tv2->getColor();//tv2的顏色是blackecho '<br>';//把tv2換成涂成白色$tv2->setColor('white');echo 'color of tv2 is: ' . $tv2->getColor();//tv2的顏色是whiteecho '<br>';echo 'color of tv1 is: ' . $tv1->getColor();//tv1的顏色是black

這段代碼的第2行,我們用clone關鍵字復制tv1,現在我們就擁有了一份真正的tv1的拷貝tv2,我們還是按照之前的方法來檢測復制是否成功。我們可以看到,我們將tv2的顏色換成了white,tv1的顏色還是black,這樣我們的復制操作就成功了。

__clone魔術方法

現在我們考慮到這樣一個情況,每一臺電視機應該都有自己的編號,這個編號如同我們的身份證號碼一樣應該是唯一的,所以當我們在復制一臺電視機的時候,我們不希望這個編號也被復制過來,以免造成一些麻煩。我們想到的一個策略是將賦值出來的電視機的編號清空,然后再按照需求來重新分配編號。

那么__clone魔術方法就是專門用來解決這樣的問題,__clone魔術方法會在對象被復制( 也就是clone操作)的時候被觸發。我們修改了電視機類Television的代碼,添加了編號屬性和__clone方法,代碼如下。

PHP代碼

/**電視機類*/class Television{  /**   電視機編號   */  protected $_identity  = 0;  /**   屏幕高度   */  protected $_screenLength = 300;  /**   屏幕寬度   */  protected $_screenHight = 200;  /**   電視機外觀顏色   */  protected $_color    = 'black';  /**   返回電視外觀顏色   */  public function getColor()  {    return $this->_color;  }  /**   設置電視機外觀顏色   */  public function setColor($color)  {    $this->_color = (string)$color;    return $this;  }  /**   返回電視機編號   */  public function getIdentity()  {    return $this->_identity;  }  /**   設置電視機編號   */  public function setIdentity($id)  {    $this->_identity = (int)$id;    return $this;  }  public function __clone()  {    $this->setIdentity(0);  }}

下面我們來復制這樣的一個電視機對象。

PHP代碼

$tv1 = new Television();$tv1->setIdentity('111111');echo 'id of tv1 is ' . $tv1->getIdentity();//111111echo '<br>';$tv2 = clone $tv1;echo 'id of tv2 is ' . $tv2->getIdentity();//0

我們生產了一臺電視機tv1 , 并且設置它的編號為111111,然后我們用clone將tv1復制得到了tv2,這個時候__clone魔術方法被觸發,此方法將直接作用與復制得到的對象tv2,我們在__clone方法中調用了setIdentity成員方法將tv2的_identity屬性清空,以便我們后面對它進行重新編號。由此我們可以看到__clone魔術方法能讓我們非常方便的在clone對象的時候做一些附加的操作。

clone操作的致命缺陷

clone真的能夠達到理想的復制效果嗎?在某些情況下,你應該會發現,clone操作并沒有我們想象中的那么完美。我們將以上的電視機類修改一下,然后做測試。

每臺電視機都會附帶一個遙控器,所以我們將會有一個遙控器類,遙控器和電視機是一種“聚合”關系(相對與“組合”關系,是一種較弱的依賴關系,因為一般情況電視機就算沒有遙控也能正常使用),現在我們的電視機對象應該都持有一個到遙控器對象的引用。下面看看代碼

PHP代碼

/**電視機類*/class Television{  /**   電視機編號   */  protected $_identity  = 0;  /**   屏幕高度   */  protected $_screenLength = 300;  /**   屏幕寬度   */  protected $_screenHight = 200;  /**   電視機外觀顏色   */  protected $_color    = 'black';  /**   遙控器對象   */  protected $_control   = null;  /**   構造函數中加載遙控器對象   */  public function __construct()  {    $this->setControl(new Telecontrol());  }  /**   設置遙控器對象   */  public function setControl(Telecontrol $control)  {    $this->_control = $control;    return $this;  }  /**   返回遙控器對象   */  public function getControl()  {    return $this->_control;  }  /**   返回電視外觀顏色   */  public function getColor()  {    return $this->_color;  }  /**   設置電視機外觀顏色   */  public function setColor($color)  {    $this->_color = (string)$color;    return $this;  }  /**   返回電視機編號   */  public function getIdentity()  {    return $this->_identity;  }  /**   設置電視機編號   */  public function setIdentity($id)  {    $this->_identity = (int)$id;    return $this;  }  public function __clone()  {    $this->setIdentity(0);  }}/**遙控器類*/class Telecontrol{}

下面復制這樣的一個電視機對象并且觀察電視機的遙控器對象。

PHP代碼

$tv1 = new Television();$tv2 = clone $tv1;$contr1 = $tv1->getControl(); //獲取tv1的遙控器contr1$contr2 = $tv2->getControl(); //獲取tv2的遙控器contr2echo $tv1;  //tv1的object id 為 #1echo '<br>';echo $contr1; //contr1的object id 為#2echo '<br>';echo $tv2;  //tv2的object id 為 #3echo '<br>';echo $contr2; //contr2的object id 為#2

經過復制之后,我們查看對象id,通過clone操作從tv1復制出了tv2,tv1和tv2的對象id分別是 1和3,這表示tv1和tv2是引用兩個不同的電視機對象,這符合clone操作的結果。然后我們分別獲取了tv1的遙控器對象contr1和tv2的遙控器對象contr2,通過查看它們的對象 id我們發現contr1和contr2的對象id都是2,這表明它們是到同一個對象的引用,也就是說我們雖然從tv1復制出tv2,但是遙控器并沒有被復制,每臺電視機都應該配有一個遙控器,而這里tv2和tv1共用一個遙控器,這顯然是不合常理的。

由此可見,clone操作有這么一個非常大的缺陷:使用clone操作復制對象時,當被復制的對象有對其它對象的引用的時候,引用的對象將不會被復制。然而這種情況又非常的普遍,現今 “合成/聚合復用”多被提倡用來代替“繼承復用”,“合成”和“聚合”就是讓一個對象擁有對另一個對象的引用,從而復用被引用對象的方法。我們在使用 clone的時候應該考慮到這樣的情況。那么在clone對象的時候我們應該如何去解決這樣的一個缺陷呢?可能你很快就想到了之前提到的__clone魔術方法,這確實是一種解決方案。

方案1:用__clone魔術方法彌補

前面我們已經介紹了__clone魔術方法的用法,我們可以在__clone方法中將被復制對象中其它對象的引用重新引用到一個新的對象。下面我們看看修改后的__clone()魔術方法:

PHP代碼

public function __clone(){  $this->setIdentity(0);  //重新設置一個遙控器對象  $this->setControl(new Telecontrol());}

第04行中我們為復制出來的電視機對象重新設置了一個遙控器,我們按照之前的方法查看對象的id可以發現,兩臺電視機的遙控器擁有不同的對象id,這樣我們的問題就解決了。

但是這樣的方式大概并不算太好,如果被復制對象中有多個到其它對象的引用,我們必須在__clone方法中逐個的重新設置,更糟糕的是如果被復制對象的類由第三方提供,我們無法修改代碼,那復制操作基本就無法順利完成了。

我們使用clone來復制對象,這種復制叫做“淺復制”:被復制對象的所有變量都含有與原來的對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。也就是說,淺復制僅僅復制所考慮的對象,而不復制它所引用的對象。相對于“淺復制”,當然也有一個“深復制”:被復制的對象的所有的變量都含有與原來的對象相同的值,除去那些引用其他對象的變量。也就是說,深復制把要復制的對象所引用的對象都復制了一遍。深復制需要決定深入到多少層,這是一個不容易確定的問題,此外可能會出現循環引用的問題,這些都必須小心處理。我們的方案2將是一個深復制的解決方案。

方案2:利用串行化做深復制

PHP有串行化(serialize)和反串行化(unserialize)函數,我們只需要用serialize()將一個對象寫入一個流,然后從流中讀回對象,那么對象就被復制了。在JAVA語言里面,這個過程叫做“冷藏”和“解凍”。下面我們將測試一下這個方法:

PHP代碼

$tv1 = new Television();$tv2 = unserialize(serialize($tv1));//序列化然后反序列化$contr1 = $tv1->getControl(); //獲取tv1的遙控器contr1$contr2 = $tv2->getControl(); //獲取tv2的遙控器contr2echo $tv1;  //tv1的object id 為 #1echo '<br>';echo $contr1; //contr1的object id 為#2echo '<br>';echo $tv2;  //tv2的object id 為 #4echo '<br>';echo $contr2; //contr2的object id 為#5

我們可以看到輸出結果,tv1和tv2擁有了不同的遙控器。這比方案1要方便很多,序列化是一個遞歸的過程,我們不需要理會被對象內部引用了多少個對象以及引用了多少層對象,我們都可以徹底的復制。注意使用此方案時我們無法觸發__clone魔術方法來完成一些附加操作,當然我們可以在深復制之后再進行一次clone操作來觸發__clone魔術方法,只是會對效率些小的影響。另外此方案會觸發被復制對象和所有被引用對象的__sleep和__wakeup魔術方法,所以這些情況都需要被考慮。

總結

不同的對象復制方式有著不同的效果,我們應該根據具體應用需求來考慮使用哪種方式以及如何改進復制方式。PHP5的面向對象特性和JAVA比較接近,相信我們可以從JAVA中借鑒很多寶貴的經驗。

希望本文所述對大家PHP程序設計有所幫助。


注:相關教程知識閱讀請移步到PHP教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
久久夜精品香蕉| 国产噜噜噜噜噜久久久久久久久| 日本精品va在线观看| 久久久99免费视频| 8x海外华人永久免费日韩内陆视频| 97在线精品国自产拍中文| 日韩在线观看视频免费| 亚洲视频一区二区| 国产精品久久久久77777| 尤物yw午夜国产精品视频| 日本91av在线播放| 精品美女永久免费视频| 国产精品视频最多的网站| 日韩有码在线观看| 国产精品日韩在线| 欧洲午夜精品久久久| 亚洲第一天堂无码专区| 日本国产高清不卡| 久久久黄色av| 精品视频—区二区三区免费| 久久精品成人动漫| 最近2019中文字幕在线高清| 精品福利在线看| 在线观看欧美www| 国模精品一区二区三区色天香| 精品久久久一区二区| 91精品中国老女人| 亚洲第一区第一页| 国产精品美乳在线观看| 欧美多人爱爱视频网站| 91成人国产在线观看| 国产欧美精品在线| 在线成人中文字幕| 51精品在线观看| 欧美黑人xxxx| 亚洲国产小视频| 国产69久久精品成人看| 亚洲成人三级在线| 91在线视频精品| 亚洲精品国产拍免费91在线| 亚洲成人黄色在线| 国产午夜精品麻豆| 最近免费中文字幕视频2019| 亚洲电影在线看| 91精品国产自产在线| 亚洲免费人成在线视频观看| 亚洲天堂成人在线| 国产精品综合不卡av| 久久精品国产亚洲| 成人网在线观看| 欧美大片免费观看| 伊人久久综合97精品| 色视频www在线播放国产成人| 欧美激情视频三区| 久久九九国产精品怡红院| 欧美国产高跟鞋裸体秀xxxhd| 美女国内精品自产拍在线播放| 久久久久久久一区二区三区| 欧美大片免费观看| 国产精品视频不卡| 久久视频精品在线| 国产精品久久久久久久av大片| 日av在线播放中文不卡| 中文字幕精品—区二区| 色偷偷噜噜噜亚洲男人| 亚洲视频axxx| 97在线免费观看视频| 国产va免费精品高清在线观看| 精品美女国产在线| 国产女人18毛片水18精品| 97久久久免费福利网址| 隔壁老王国产在线精品| 亚洲第一黄色网| 欧美高清视频一区二区| 国产成+人+综合+亚洲欧美丁香花| 欧美一区二区三区免费观看| 国产成人激情小视频| 亚洲性视频网站| 国产成人精品电影| 欧美另类第一页| 国产91网红主播在线观看| 亚洲最大成人免费视频| 亚洲国产精品国自产拍av秋霞| 欧美日韩在线一区| 亚洲欧美另类自拍| 久久久精品国产网站| 91久久久久久久| 亚洲一区二区三区视频| 亚洲第一免费网站| 国产美女精彩久久| 国产噜噜噜噜久久久久久久久| 欧美一级淫片播放口| 亚洲欧洲国产伦综合| 97国产精品人人爽人人做| 91精品国产91久久久久久久久| 91精品国产91| 久久久久久久国产精品视频| 久久综合国产精品台湾中文娱乐网| 原创国产精品91| 国产日韩视频在线观看| 国内精品模特av私拍在线观看| 欧美一级视频一区二区| 国产精品美女网站| 亚洲女性裸体视频| 美女精品视频一区| 欧美激情第一页xxx| 久久久极品av| 亚洲专区中文字幕| 国产亚洲视频在线| 亚洲精品久久久久中文字幕二区| 亚洲伊人久久大香线蕉av| 久久国产精品久久久久久久久久| 日韩美女在线看| 欧美精品videossex88| 亚洲激情成人网| 亚洲欧美另类自拍| 欧美极品美女视频网站在线观看免费| 欧美日韩在线免费| 在线观看国产精品日韩av| 国产精品美女久久久久久免费| 欧美视频免费在线观看| 国产视频久久久久久久| 国产69精品99久久久久久宅男| 亚洲欧美日本精品| 成人激情在线观看| 欧美一区二区三区图| 成人xxxx视频| 久热国产精品视频| 亚洲国产日韩欧美综合久久| 91国内揄拍国内精品对白| 成人xxxx视频| 欧美激情啊啊啊| 欧美激情精品久久久| 91久久精品美女| 欧美一区二区视频97| 国产精品欧美一区二区| 亚洲欧美综合v| 日本a级片电影一区二区| 欧美亚洲国产另类| 亚洲色图偷窥自拍| 欧美日韩国产页| 国产在线精品自拍| 亚洲精品国产精品自产a区红杏吧| 色婷婷综合久久久久中文字幕1| 国产91色在线|免| 美女久久久久久久久久久| 亚洲成人精品av| 精品亚洲夜色av98在线观看| 国产精品久久久久99| 亚洲精品美女久久久久| 久久人人97超碰精品888| 欧美电影在线观看网站| 国产精品久久久久久久久久| 中文在线资源观看视频网站免费不卡| 久久久精品日本| 久久亚洲国产精品成人av秋霞| 国产精品看片资源| 91视频国产高清| 尤物yw午夜国产精品视频明星| 91大神福利视频在线| 久久久久国产精品免费网站| 欧美高清在线观看| 成人信息集中地欧美| 69av视频在线播放|