PHP中的对象复制及__clone() 函数
2016-09-12 13:28
302 查看
http://www.androiddev.net/php-object-clone-copy-constructor/
在PHP中, 对象间的赋值操作实际上是引用操作 (事实上,绝大部分的编程语言都是如此! 主要原因是内存及性能的问题) , 比如 :
因为$obj1和$obj2都是指向同一个内存区的引用,所以修改任何一个对象都会同时修改另外一个对象。
在有些时候,我们其实不希望这种reference式的赋值方式, 我们希望能完全复制一个对象,这是侯就需要用到 Php中的clone (对象复制)。
因为clone的方式实际上是对整个对象的内存区域进行了一次复制并用新的对象变量指向新的内存, 因此赋值后的对象和源对象相互之间是基本来说独立的。
什么? 基本独立?!这是什么意思? 因为PHP的object clone采用的是浅拷贝(shallow copy)的方法, 如果对象里的属性成员本身就是reference类型的,clone以后这些成员并没有被真正复制,仍然是引用的。 (事实上,其他大部分语言也是这样实现的, 如果你对C++的内存,拷贝,copy constructor等概念比较熟悉,就很容易理解这个概念), 下面是一个例子来说明:
我们再举一个更实用的例子来说明一下PHP clone这种浅拷贝带来的后果:
这一下可以更加清楚的看到问题了吧。 一般来讲,你用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概念上有些相似,但执行流程并不一样)
关于 __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方法可以做很多事情,但我目前能想到的就只有把 浅拷贝变成深拷贝 这个场景的应用了, 如果有其他用法,欢迎大家提出来。
在PHP中, 对象间的赋值操作实际上是引用操作 (事实上,绝大部分的编程语言都是如此! 主要原因是内存及性能的问题) , 比如 :
1 | class myclass { |
2 | public $data ; |
3 | } |
4 | $obj1 = new myclass(); |
5 | $obj1 ->data = "aaa" ; |
6 | $obj2 = $obj1 ; |
7 | $obj2 ->data = "bbb" ; //$obj1->data的值也会变成"bbb" |
在有些时候,我们其实不希望这种reference式的赋值方式, 我们希望能完全复制一个对象,这是侯就需要用到 Php中的clone (对象复制)。
1 | class myclass { |
2 | public $data ; |
3 | } |
4 | $obj1 = new myclass(); |
5 | $obj1 ->data = "aaa" ; |
6 | $obj2 =clone $obj1 ; |
7 | $obj2 ->data = "bbb" ; // $obj1->data的值仍然为"aaa" |
什么? 基本独立?!这是什么意思? 因为PHP的object clone采用的是浅拷贝(shallow copy)的方法, 如果对象里的属性成员本身就是reference类型的,clone以后这些成员并没有被真正复制,仍然是引用的。 (事实上,其他大部分语言也是这样实现的, 如果你对C++的内存,拷贝,copy constructor等概念比较熟悉,就很容易理解这个概念), 下面是一个例子来说明:
1 | class myClass{ |
2 | public $data ; |
3 | } |
4 |
5 | $sss = "aaa" ; |
6 | $obj1 = new myClass(); |
7 | $obj1 ->data =& $sss ; //注意,这里是个reference! |
8 | $obj2 =clone $obj1 ; |
9 | $obj2 ->data= "bbb" ; //这时,$obj1->data的值变成了"bbb" 而不是"aaa"! |
10 |
11 | var_dump( $obj1 ); |
12 | var_dump( $obj2 ); |
1 | class testClass |
2 | { |
3 | public $str_data ; |
4 | public $obj_data ; |
5 | } |
6 |
7 | $dateTimeObj
new DateTime( "2014-07-05" , new DateTimeZone( "UTC" )); |
8 |
9 | $obj1 = new testClass(); |
10 | $obj1 ->str_data = "aaa" ; |
11 | $obj1 ->obj_data = $dateTimeObj ; |
12 |
13 | $obj2 =clone $obj1 ; |
14 |
15 | var_dump( $obj1 ); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" |
16 | var_dump( $obj2 ); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" |
17 |
18 | $obj2 ->str_data = "bbb" ; |
19 | $obj2 ->obj_data->add( new DateInterval( 'P10D' )); //给$obj2->obj_date 的时间增加了10天 |
20 |
21 | var_dump( $obj1 ); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" !!!! |
22 | var_dump( $obj2 ); // str_data:"bbb" obj_data:"2014-07-15 00:00:00" |
23 | var_dump( $dateTimeObj ) // 2014-07-15 00:00:00" |
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概念上有些相似,但执行流程并不一样)
1 | class testClass |
2 | { |
3 | public $str_data ; |
4 | public $obj_data ; |
5 |
6 | public function __clone() { |
7 | $this ->obj_data =clone $this ->obj_data; |
8 | } |
9 |
10 | $dateTimeObj
new DateTime( "2014-07-05" , new DateTimeZone( "UTC" )); |
11 |
12 | $obj1 = new testClass(); |
13 | $obj1 ->str_data = "aaa" ; |
14 | $obj1 ->obj_data = $dateTimeObj ; |
15 |
16 | $obj2 =clone $obj1 ; |
17 | var_dump( $obj1 ); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" |
18 | var_dump( $obj2 ); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" |
19 | $obj2 ->str_data = "bbb" ; |
20 | $obj2 ->obj_data->add( new DateInterval( 'P10D' )); |
21 |
22 | var_dump( $obj1 ); // str_data:"aaa" obj_data:"2014-07-05 00:00:00" |
23 | var_dump( $obj2 ); // str_data:"aaa" obj_data:"2014-07-15 00:00:00" |
24 | var_dump( $dateTimeObj ); //"2014-07-05 00:00:00" |
按照这个定义,事实上__clone方法可以做很多事情,但我目前能想到的就只有把 浅拷贝变成深拷贝 这个场景的应用了, 如果有其他用法,欢迎大家提出来。
相关文章推荐
- php之clone 复制对象以及__clone魔术方法
- 关于PHP中的对象(类的实例)的复制、clone(克隆)
- php 对象复制(克隆)clone()
- 【PHP】对象的复制(拷贝)与__clone()方法
- [PHP] 对象的浅复制与深复制-----“=” 与 “clone”
- PHP使用clone关键字复制对象
- 复制对象句柄--D u p l i c a t e H a n d l e 函数
- 小议Function.apply() 之一------(函数的劫持与对象的复制)
- PHP对象转成数组的函数
- PHP中对象的clone和引用的区别(Object Cloning and Passing by Reference in PHP)
- PHP操作文件类的函数代码(文件和文件夹创建,复制,移动和删除)
- php对象几个常见函数
- 摘自PHP手册[3] – Classes/Objects 类/对象函数
- php的引用(就是在变量或者函数、对象等前面加上&符号)
- PHP对象转换为数组函数(递归方法)
- PHP var_dump遍历对象属性的函数与应用代码
- php 函数引用、变量引用、对象引用的区别
- PHP 5.0对象模型深度探索之对象复制
- php引用(&)变量引用,函数引用,对象引用和参数引用用法详解