當在使用某個物件,而需要對該物件進行大量操作,或者在新的上下文環境中複用該物件的部分或全部資料時,需要對其進行拷貝操作。
拷貝的深淺
- 我們日常所使用的拷貝,如一個資料夾,或者視訊,音樂的拷貝,使用時可以發現這種拷貝是真正的生成了一個新的檔案,佔用了部分記憶體的。那麼這種就是深拷貝,也就是在計算機中重新開闢了一塊新的記憶體地址用於存放物件。
而什麼是淺拷貝,淺拷貝只是拷貝了基本的資料型別,而引用資料型別,複製後也是會發生引用,淺拷貝只是指向被複制的記憶體地址。如果引用資料型別的物件發生了改變,那麼淺拷貝出來的物件也會改變
由淺入深
那麼結合程式碼來看看從淺拷貝到深拷貝發生了什麼
<?php
class People {
protected $_name = `張三`;
protected $_sex = `男`;
protected $_age = `18`;
/**
* return Name
*/
public function getName() {
return $this-> _name;
}
/**
* set name
*/
public function setName($name) {
$this->_name = (string)$name;
return $this;
}
}
$p1 = new People();
$p2 = $p1;
複製程式碼
上面建立了一個people類,然後例項化了p1,用賦值的方式 建立了p2 這樣就得到了兩個名字,年齡,性別都一樣的人。那麼當我們修改p1或p2時都會改變name
echo $p1->getName();
echo $p2->getName();
//p2改名
$p2->setName(`李四`);
echo $p1->getName();
echo $p2->getName();
//p1改名
$p1->setName(`王五`);
echo $p1->getName();
echo $p2->getName();
複製程式碼
輸出為:張三張三李四李四王五王五
這裡物件的賦值和傳值都是以引用的方式。名字雖然不同但指的是同一個人。所以這種拷貝相當於一個人有個名字和外號,人還是這個人,這種拷貝不是我所想要的。那麼換種方式。用clone來複制物件。
clone函式
$p1 = new People();
$p2 = clone $p1;
echo $p1->getName();
echo $p2->getName();
//p2改名
$p2->setName(`李四`);
echo $p1->getName();
echo $p2->getName();
//p1改名
$p1->setName(`王五`);
echo $p1->getName();
echo $p2->getName();
複製程式碼
那麼這段程式碼用clone關鍵字複製p1物件,現在這個p1物件得到了這個真正的拷貝p2,p1和p2改名時分別都能改成功,而p1和p2都屬於不同的物件,都是獨立的個體了,如果p1有其他的關係,那麼如何呢,就如同你克隆了一個人,儘管這個克隆人和本體的基本屬性相同,但會有相同的記憶嗎會有複雜的社會關係嗎?
顯然並不會
那麼假如這個張三這個人有同學這個類,現在如何讓克隆體也有這個同學類呢。下面精簡一下程式碼
class People {
public $name = `張三`;
public $mate;
/**
* 建構函式中載入同學物件
*/
public function __construct()
{
$this->mate = new Classmate();
}
}
/**
* 同學類
*/
class Classmate {
public $name ="王五";
}
$p1 = new People();
$p2 = clone $p1;
$p2 ->name = "ll";
echo $p1->name;
echo $p2->name;
$p2->mate->name = "ll";
echo $p1->mate->name;
複製程式碼
然後可以發現p2可以改名字也就是p2的普通屬性實現了深拷貝 而mate物件屬性中的名字也會改變,存在一定的問題
一般有兩種解決方法:
1.重寫clone函式
class People {
public $name = `張三`;
public $mate;
/**
* 建構函式中載入同學物件
*/
public function __construct()
{
$this->mate = new Classmate();
}
//重寫clone函式
public function __clone() {
$this->mate = clone $this->mate;
}
}
/**
* 同學類
*/
class Classmate {
public $name ="王五";
}
$p1 = new People();
$p2 = clone $p1;
$p2 ->name = "ll";
echo $p1->name;
echo $p2->name;
$p2->mate->name = "ll";
echo $p1->mate->name;//輸出還是王五
複製程式碼
這樣就可以解決了,但是如果classmate中有很多屬性,或者people類要引入很多類,那麼這樣的重寫clone函式就會變得很麻煩
2序列化和反序列化
這種方法不用修改函式比較簡單
<?php
class People {
public $name = `張三`;
public $mate;
/**
* 建構函式中載入同學物件
*/
public function __construct()
{
$this->mate = new Classmate();
}
}
/**
* 同學類
*/
class Classmate {
public $name ="王五";
}
$p1 = new People();
$p2 = serialize($p1);
$p2 = unserialize($p2);
$p2 ->name = "ll";
$p2->mate->name = "ll";
echo $p1->mate->name;
複製程式碼
還可以用json_encode之後再json_decode,實現賦值和法二一樣