首頁 > php教程 > php手册 > 主體

【PHP】物件的複製(拷貝)與__clone()方法

WBOY
發布: 2016-09-01 00:00:45
原創
1356 人瀏覽過

參考連結:

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 } } 
登入後複製

 

來源:php.cn
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
熱門推薦
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!