程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 網頁編程 >> PHP編程 >> 關於PHP編程 >> 【PHP】對象的復制(拷貝)與__clone()方法,php__clone

【PHP】對象的復制(拷貝)與__clone()方法,php__clone

編輯:關於PHP編程

【PHP】對象的復制(拷貝)與__clone()方法,php__clone


參考鏈接:

1、php.net官網文檔 - 對象復制

 

什麼時候用到?摘自php.net:

在多數情況下,我們並不需要完全復制一個對象來獲得其中屬性。但有一個情況下確實需要:如果你有一個 GTK 窗口對象,該對象持有窗口相關的資源。你可能會想復制一個新的窗口,保持所有屬性與原來的窗口相同,但必須是一個新的對象(因為如果不是新的對象,那麼一個窗口中的改變就會影響到另一個窗口)。還有一種情況:如果對象 A 中保存著對象 B 的引用,當你復制對象 A 時,你想其中使用的對象不再是對象 B 而是 B 的一個副本,那麼你必須得到對象 A 的一個副本。

 

嘗試使用最簡單的“=”

首先要明確的是:php的對象是以一個標識符來存儲的,所以對對象的直接“賦值”行為相當於“傳引用”

<?php

function dump($var){
    var_dump($var);
    echo "<br/>";
}

class A{
    private $a;
    protected $b;
    public $c;

    public function d(){
        echo "A -> d";
    }
}

$a1 = new A();
$a2 = $a1;
$a3 = new A();
dump($a1);
dump($a2);
dump($a3);

 

輸出的結果是:

object(A)#1 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 
object(A)#1 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 
object(A)#2 (3) { ["a":"A":private]=> NULL ["b":protected]=> NULL ["c"]=> NULL } 

其中可以注意到,作為對象標識符的#n,顯示$a1和$a2其實是指向同一個對象,而$a3是另一個對象

所以,如果需要拷貝一個相同且全新的對象,不能直接通過=來復制,否則改變了$a1->a就相當於修改了$a2->a。

 

淺拷貝

PHP5中,類中有個魔術方法__clone(),在配合clone關鍵字和對象使用的時候,會自動調用(如果沒有顯式定義,則調用空的方法)。

clone關鍵字的作用是,復制某一個對象形成一個對象的“淺拷貝”,然後賦值給新的對象,此時對象標識符不同了!

<?php

function dump($var){
    var_dump($var);
    echo "<br/>";
}

class B{
    public $d;
}

class A{
    public $a;
    public $b;

    public function d(){
        echo "A -> d";
    }
}

$a1 = new A();
$a1->a = 123;
// 這裡對象屬性的值是一個對象示例,其實就是存儲了對象標識符。使用clone關鍵字生成的拷貝中的b屬性仍然指向舊對象的b屬性指向的對象,這是"淺拷貝"出現的問題。如果需要指向一個新的對象,必須"深拷貝"
$a1->b = new B();

// PHP 5 only
$a2 = clone $a1;

dump($a1);
dump($a2);

 

輸出的結果是:

object(A)#1 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 
object(A)#3 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 

可以看到,$a1和$a2明顯是兩個不同的對象(對象標識符不同了)。但是需要留意的一點是,"b"指向的對象標識符都是#2,證明這兩個對象是相同的,這就是“淺拷貝”的“缺陷”——但有時候這兩個對象確實需要相同,所以PHP的clone默認是“淺拷貝”。

 

為什麼叫淺拷貝(shallow copy)?

因為在復制的時候,所有的屬性都是“值傳遞”的,而上面的b屬性存儲的是對象標識符,所以相當於做了“引用傳遞”,這並不是完全的拷貝,所以稱為“淺拷貝”。

 

深拷貝

上面講到,使用clone關鍵字的時候,會自動調用舊對象的__clone()方法(然後返回拷貝的對象),所以只需要在對應的類中重寫__clone()方法,使返回的對象中的“引用傳遞”的屬性指向另一個新的對象。以下是例子(可以比較“淺拷貝”的例子,其實就多了重寫__clone()的步驟):

<?php

function dump($var){
    var_dump($var);
    echo "<br/>";
}

class B{
    public $d;
}

class A{
    public $a;
    public $b;

    public function d(){
        echo "A -> d";
    }

    public function __clone(){
        // clone自己
        $this->b = clone $this->b;
    }
}

$a1 = new A();
$a1->a = 123;
// 這裡對象屬性的值是一個對象示例,其實就是存儲了對象標識符。使用clone關鍵字生成的拷貝中的b屬性仍然指向舊對象的b屬性指向的對象,這是"淺拷貝"出現的問題。如果需要指向一個新的對象,必須"深拷貝"
$a1->b = new B();

// PHP 5 only
$a2 = clone $a1;

dump($a1);
dump($a2);

 

結果就不同了,注意b屬性的對象標識符:

object(A)#1 (2) { ["a"]=> int(123) ["b"]=> object(B)#2 (1) { ["d"]=> NULL } } 
object(A)#3 (2) { ["a"]=> int(123) ["b"]=> object(B)#4 (1) { ["d"]=> NULL } } 

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved