PHP中對象的深拷貝與淺拷貝

先說一下深拷貝和淺拷貝通俗理解php

深拷貝:賦值時值徹底複製,徹底的copy,對其中一個做出改變,不會影響另外一個json

淺拷貝:賦值時,引用賦值,至關於取了一個別名。對其中一個修改,會影響另外一個函數

PHP中, = 賦值時,普通對象是深拷貝,但對對象來講,是淺拷貝。也就是說,對象的賦值是引用賦值。(對象做爲參數傳遞時,也是引用傳遞,不管函數定義時參數前面是否有&符號)性能

 

php4中,對象的 = 賦值是實現一份副本,這樣存在不少問題,在不知不覺中咱們可能會拷貝不少份副本。this

php5中,對象的 = 賦值和傳遞都是引用。要想實現拷貝副本,php提供了clone函數實現。spa

clone徹底copy了一份副本。可是clone時,咱們可能不但願copy源對象的全部內容,那咱們能夠利用__clone來操做。code

在__clone()中,咱們能夠進行一些操做。注意,這些操做,也就是__clone函數是做用於拷貝的副本對象上的對象

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<?php
//普通對象賦值,深拷貝,徹底值複製
$m  = 1;
$n  $m ;
$n  = 2;
echo  $m ; //值複製,對新對象的改變不會對m做出改變,輸出 1.深拷貝
echo  PHP_EOL;
/*==================*/
 
//對象賦值,淺拷貝,引用賦值
class  Test{
     public  $a =1;
}
$m  new  Test();
$n  $m ; //引用賦值
$m ->a = 2; //修改m,n也隨之改變
echo  $n ->a; //輸出2,淺拷貝
echo  PHP_EOL;
?>

  因爲對象的賦值時引用,要想實現值複製,php提供了clone函數來實現複製對象。blog

可是clone函數存在這麼一個問題,克隆對象時,原對象的普通屬性能值複製,可是源對象的對象屬性賦值時仍是引用賦值,淺拷貝。ci

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
<?php
class  Test{
     public  $a =1;
}
 
class  TestOne{
     public  $b =1;
     public  $obj ;
     //包含了一個對象屬性,clone時,它會是淺拷貝
     public  function  __construct(){
         $this ->obj =  new  Test();
     }
}
$m  new  TestOne();
$n  $m ; //這是徹底的淺拷貝,不管普通屬性仍是對象屬性
 
$p  clone  $m ;
 
//普通屬性實現了深拷貝,改變普通屬性b,不會對源對象有影響
$p ->b = 2;
echo  $m ->b; //輸出原來的1
echo  PHP_EOL;
 
//對象屬性是淺拷貝,改變對象屬性中的a,源對象m中的對象屬性中a也改變
 
$p ->obj->a = 3;
echo  $m ->obj->a; //輸出3,隨新對象改變
?>

  要想實現對象真正的深拷貝,有下面兩種方法:

寫clone函數:以下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php
class  Test{
     public  $a =1;
}
 
class  TestOne{
     public  $b =1;
     public  $obj ;
     //包含了一個對象屬性,clone時,它會是淺拷貝
     public  function  __construct(){
         $this ->obj =  new  Test();
     }
     
     //方法一:重寫clone函數
     public  function  __clone(){
         $this ->obj =  clone  $this ->obj;
     }
}
 
$m  new  TestOne();
$n  clone  $m ;
 
$n ->b = 2;
echo  $m ->b; //輸出原來的1
echo  PHP_EOL;
//能夠看到,普通屬性實現了深拷貝,改變普通屬性b,不會對源對象有影響
 
//因爲改寫了clone函數,如今對象屬性也實現了真正的深拷貝,對新對象的改變,不會影響源對象
$n ->obj->a = 3;
echo  $m ->obj->a; //輸出1,不隨新對象改變,仍是保持了原來的屬性
 
?>

  改寫__clone()函數不太方便,並且你得在每一個類中把這個類裏面的對象屬性都在__clone()中 一一 clone

第二種方法,利用序列化反序列化實現,這種方法實現對象的深拷貝簡單,不須要修改類

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<?php
class  Test{
     public  $a =1;
}
 
class  TestOne{
     public  $b =1;
     public  $obj ;
     //包含了一個對象屬性,clone時,它會是淺拷貝
     public  function  __construct(){
         $this ->obj =  new  Test();
     }
     
}
 
$m  new  TestOne();
//方法二,序列化反序列化實現對象深拷貝
$n  = serialize( $m );
$n  = unserialize( $n );
 
$n ->b = 2;
echo  $m ->b; //輸出原來的1
echo  PHP_EOL;
//能夠看到,普通屬性實現了深拷貝,改變普通屬性b,不會對源對象有影響
 
 
$n ->obj->a = 3;
echo  $m ->obj->a; //輸出1,不隨新對象改變,仍是保持了原來的屬性,能夠看到,序列化和反序列化能夠實現對象的深拷貝
 
?>

 還有第三種方法,其實和第二種相似,json_encode以後再json_decode,實現賦值 

相關文章
相關標籤/搜索