先說一下深拷貝和淺拷貝通俗理解
深拷貝:賦值時值完全復制,完全的copy,對其中一個作出改變,不會影響另一個
淺拷貝:賦值時,引用賦值,相當於取了一個別名。對其中一個修改,會影響另一個
PHP中, = 賦值時,普通對象是深拷貝,但對對象來說,是淺拷貝。也就是說,對象的賦值是引用賦值。(對象作為參數傳遞時,也是引用傳遞,無論函數定義時參數前面是否有&符號)
php4中,對象的 = 賦值是實現一份副本,這樣存在很多問題,在不知不覺中我們可能會拷貝很多份副本。
php5中,對象的 = 賦值和傳遞都是引用。要想實現拷貝副本,php提供了clone函數實現。
clone完全copy了一份副本。但是clone時,我們可能不希望copy源對象的所有內容,那我們可以利用__clone來操作。
在__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函數來實現復制對象。
但是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
|
<?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,實現賦值