前段時間頭一次聽說淺復制與深復制了,當時就是看的java例子,下文我來為各位分享一些小編總結的php中淺復制與深復制的例子供各位學習.
周末閑來無事看到了原型模式,其中談到了淺復制和深復制,想到PHP中的對應賦值、克隆以及克隆是淺復制還是深復制.
先來看看賦值,例如有一個簡歷類,有身高和體重兩個屬性:
- class Resume
- {
- public $height;
- public $weight;
- public $workExperience;
- }
- $ResumeA = new Resume();
- $ResumeB = $ResumeA;
此時實例化了一個Resume類并賦值給了$ResumeA變量,然后將$ResumeA變量賦值給$ResumeB,PHP手冊上有說.
自PHP5起,new運算符自動返回一個引用,一個對象變量已經不再保存整個對象的值,只是保存一個標識符來訪問真正的對象內容,當對象作為參數傳遞,作為結果返回,或者賦值給另外一個變量,另外一個變量跟原來的不是引用的關系,只是他們都保存著同一個標識符的拷貝,這個標識符指向同一個對象的真正內容.
所以若通過$ResumeB修改height屬性,則$ResumeA也會跟著變,如果想要復制一個全新的對象,則可以通過clone來實現,如.
$ResumeB = clone $ResumeA;
此時將$ResumeA的值拷貝到新的變量$ResumeB中,改變其中一個不影響另一個,修改$ResumeB中height屬性,$ResumeA不會跟著改變。
但如果該類引用了其他對象,則所有的引用仍然指向到原來的對象。clone的這種復制方式就是淺復制。被賦值對象的所有變量都還有與原來對象相同的值,而所有的對其他對象的引用都仍然指向原來的對象。
如果上面類中workExperience為WorkExperience類的引用,當克隆的時候,克隆前后的workExperience屬性還是指向到同一個對象內容.
與淺復制對應的是深復制,深復制把引用對象的變量指向復制過的新對象,而不是原有的被引用的對象。
PHP中可以通過兩種方式來實現深復制,第一種是__clone魔術方法:
- public function __clone()
- {
- $this->workExperience = new WorkExperience();
- }
深復制涉及深的層次,通過clone魔術方法實現需要知道有幾層然后對每一層依次實現。還有一種是可以通過序列化對象的方式,先將對象序列化之后再反序列化,如:
$ResumeB = unserialize(serialize($ResumeA));
clone還算常用的拷貝方式,整理的目的只是為了記錄一下clone是淺復制,需要注意一下對象的引用.
我們再舉一個更實用的例子來說明一下PHP clone這種淺拷貝帶來的后果:
- class testClass
- {
- public $str_data;
- public $obj_data;
- }
- $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC"));
- $obj1 = new testClass();
- $obj1->str_data ="aaa";
- $obj1->obj_data = $dateTimeObj;
- $obj2 = clone $obj1;
- var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" //開源軟件:Vevb.com
- var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
- $obj2->str_data ="bbb";
- $obj2->obj_data->add(new DateInterval('P10D')); //給$obj2->obj_date 的時間增加了10天
- var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" !!!!
- var_dump($obj2); // str_data:"bbb" obj_data:"2014-07-15 00:00:00"
- var_dump($dateTimeObj) // 2014-07-15 00:00:00"
這一下可以更加清楚的看到問題了吧,一般來講,你用clone來復制對象,希望是把兩個對象徹底分開,不希望他們之間有任何關聯,但由于clone的shallow copy的特性,有時候會出現非你期望的結果,上面的例子中.
1) $obj1->obj_data =$dateTimeObj 這句話實際上是個引用類型的賦值. 還記得前面提到的PHP中對象直接的賦值是引用操作么?除非你用$obj1->obj_dat = clone $dataTimeObj!
2) $obj2 = clone $obj1 這句話生成了一個obj1對象的淺拷貝對象,并賦給obj2. 由于是淺拷貝,obj2中的obj_data也是對$dateTimeObj的引用!
3)$dateTimeObj, $obj1->obj_data, $obj2->obj_data 實際上是同一個內存區對象數據的引用,因此修改其中任何一個都會影響其他兩個!
如何解決這個問題呢?采用PHP中的 __clone方法 把淺拷貝轉換為深拷貝(這個方法給C++中的copy constructor概念上有些相似,但執行流程并不一樣).
- class testClass
- {
- public $str_data;
- public $obj_data;
- public function __clone() {
- $this->obj_data = clone $this->obj_data;
- }
- $dateTimeObj = new DateTime("2014-07-05", new DateTimeZone("UTC"));
- $obj1 = new testClass();
- $obj1->str_data ="aaa";
- $obj1->obj_data = $dateTimeObj;
- $obj2 = clone $obj1;
- var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" //開源軟件:Vevb.com
- var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
- $obj2->str_data ="bbb";
- $obj2->obj_data->add(new DateInterval('P10D'));
- var_dump($obj1); // str_data:"aaa" obj_data:"2014-07-05 00:00:00"
- var_dump($obj2); // str_data:"aaa" obj_data:"2014-07-15 00:00:00"
- var_dump($dateTimeObj); //"2014-07-05 00:00:00"
關于 __clone() , PHP官方的文檔:Once the cloing is complete, if a __clone() method is defined,then the newly created object’s __clone() method will be called,to allow any necessary properties that need to be changed.
按照這個定義,事實上__clone方法可以做很多事情,但我目前能想到的就只有把 淺拷貝變成深拷貝 這個場景的應用了,如果有其他用法,歡迎大家提出來.
新聞熱點
疑難解答