对象复制 写时复制

问:为什么关于对象复制有那么多的迷惑点呢?怎么对对象赋值有个清晰的认识呢?

答:关于对象复制的确是个难点,主要是他和普通的变量对待复制的表现不同,而且又牵扯到深复制和浅复制的相关东西,由此带来的区别,然而他们又都完成了一个目标就是复制。所以让人有点迷惑,其实掌握几个关键知识点,你就明白复制的原理了

 

问:那能说一下有关复制的相关原理么?

答:恩,这话说起来就长了,而且对不同语言来说,甚至有点大相径庭。咱这里就只说php,以php的语言特性来说,较之其他语言有冲突的地方,我们不做比较了。只需要记住,php是这样的就ok。

我们先来说一下php这门语言,它是一门弱类型语言(不必向 PHP 声明该变量的数据类型

PHP 会根据变量的值,自动把变量转换为正确的数据类型。在强类型的编程语言中,我们必须在使用变量前先声明(定义)变量的类型和名称这也另一种角度说明了变量和变量值的关系,他们是两个人,不是一个人),是一门面向对象的语言。若大抵说一下他的数据类型组成String(字符串), Integer(整型), Float(浮点型), Boolean(布尔型), Array(数组), Object(对象), NULL(空值)

然后我们要理解一种概念叫引用,什么叫引用呢?我们常在写作文的时候引用别人的名人名言,那是不是这种引用呢?只能说不太准确,为什么说不太准确呢。打个比方来说吧,引用这个操作就是把某个东西管控权交给你了。我们把这个东西比喻成一间房子,控制权比作一把钥匙,谁引用了这间房子,就相当于谁有一把钥匙,自然而然,所有引用这间房子的人,都有这间房子的控制权,而且相互影响,比如说,你在厕所拉个屎,然后不冲,别人再进去就受到影响了,直白讲,大家共享这间房子的一切一切。总结来说:在 PHP 中引用意味着用不同的名字访问同一个内容,不论你用哪个名字对内容做出了运算,其他名字访问的内容也将改变

 

问:为什么关于对象复制有那么多的迷惑点呢?怎么对对象赋值有个清晰的认识呢?

答:关于对象复制的确是个难点,主要是他和普通的变量对待复制的表现不同,而且又牵扯到深复制和浅复制的相关东西,由此带来的区别,然而他们又都完成了一个目标就是复制。所以让人有点迷惑,其实掌握几个关键知识点,你就明白复制的原理了

 

问:那能说一下有关复制的相关原理么?

答:恩,这话说起来就长了,而且对不同语言来说,甚至有点大相径庭。咱这里就只说php,以php的语言特性来说,较之其他语言有冲突的地方,我们不做比较了。只需要记住,php是这样的就ok。

我们先来说一下php这门语言,它是一门弱类型语言(不必向 PHP 声明该变量的数据类型

PHP 会根据变量的值,自动把变量转换为正确的数据类型。在强类型的编程语言中,我们必须在使用变量前先声明(定义)变量的类型和名称这也另一种角度说明了变量和变量值的关系,他们是两个人,不是一个人),是一门面向对象的语言。若大抵说一下他的数据类型组成String(字符串), Integer(整型), Float(浮点型), Boolean(布尔型), Array(数组), Object(对象), NULL(空值)

然后我们要理解一种概念叫引用,什么叫引用呢?我们常在写作文的时候引用别人的名人名言,那是不是这种引用呢?只能说不太准确,为什么说不太准确呢。打个比方来说吧,引用这个操作就是把某个东西管控权交给你了。我们把这个东西比喻成一间房子,控制权比作一把钥匙,谁引用了这间房子,就相当于谁有一把钥匙,自然而然,所有引用这间房子的人,都有这间房子的控制权,而且相互影响,比如说,你在厕所拉个屎,然后不冲,别人再进去就受到影响了,直白讲,大家共享这间房子的一切一切。总结来说:在 PHP 中引用意味着用不同的名字访问同一个内容,不论你用哪个名字对内容做出了运算,其他名字访问的内容也将改变

 

为什么要先理解引用呢,因为我们接下来要说的是与引用不同的另一种手法。先来看一段代码。

$a = 23;  | $a = 23;

$b = $a; | $b = &$a;

$b = 42; | $b = 42;

var_dump($a); // int(23) | var_dump($a)://int(42)

var_dump($b); // int(42) | var_dump($b);//int(42)

 

这是为什么?这就是两种不同的机制,一种是值传递(拷贝),一种是地址传递(引用)。对于php的普通变量来说,采用的而是值传递。就是一个拷贝,完全独立的一个拷贝。关于如何破除引用产生二者公用一个内容,就是使用unset销毁其中一个变量,但另一个不受影响。因为事实上,unset是切断了引用(断开了变量名和变量内容之间的绑定,这并不意味着变量内容被销毁了,可以使用变量=null 的方法来强制释放其内存)。在php引用存在中,一个变量值是否该被回收取决于引用计数。在 PHP 中,引用不仅能用在普通语句中,还能用于函数参数和返回值

函数的引用传递(参数前面加&+函数的引用返回(函数名前面加&)

如:function &foo(&$param) {

 $param = 42;

 return $param;

}

根据官网介绍,只有a = &test($b);的形式得到的才是函数的引用返回。不加&的只是普通的返回值赋值。通过a=&test()方式调用的函数 他的作用是 将return param中的param量的内存地址与a变量的内存地址 指向了同一个地方。即产生了相当于这样的效果(a=&param;) ,这个多用在对象中。

还能用于对象中,在PHP5中 对象的赋值是个引用的过程.b=new a();  c=b;  其实等效于b=new a(); c= &b;(已过时,会出E_STRICT 级别的消息)

PHP5中默认就是通过引用来调用对象 也就是说在php5中,你不需要额外添加什么东西就可到达“对象引用”的功能, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP5定义了一个特殊的方法,称为__clone。

PHP 5 起new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。在php4中,对象的赋值是个拷贝过程,两个相互独立互不影响。

 

这里还要单独说一下global 引用,这是对全局变量的一个引用,用代码来表述就是,一下语句是对等的。global $var$var =& $GLOBALS["var"];这意味着,例如,unset $var 不会 unset 全局变量。  如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见(也就是局部变量)。可以通过使用 $GLOBALS 数组避免这一点。

还有就是$this,在一个对象的方法中,$this 永远是调用它的对象的引用

 

注意一个概念 “写时复制”。我的理解就是偷懒。比如下面的代码。

$a = array('a','c'...'n');$b = $a;//这是zend会偷个懒,不同变量指向同一个内存地址。

function printArray(&$arr){ //引用传递

   print(count($arr));

}

 printArray($a);//这时zend就会真正的给b分配内容,与a区分开。

还有一种情况,没有产生写时复制。代码如下

$foo['love'] = 1; 
$bar  = &$foo['love']; 
$tipi = $foo; 
$tipi['love'] = '2'; //原样复制,但里面有引用
echo $foo['love'];

这种情况$bar变量的引用操作,将$foo['love']污染变成了引用(两者相互引用),从而Zend没有对$tipi['love']的修改产生内存的复制分离。

还有个关于foreach中的写时复制的小知识点:

$arr = array('a','b','c','d');

foreach($arr as $key=> $value){
    $arr[$key] = $value;
}
print_r(current($arr));//echo 出 b

原因在foreach实现原理上

<?php 
//伪代码
$arr = array('a','b','c');
/* foreach循环开始*/
//first loop
$arrCopy = $arr; //复制出一个待循环数组的副本,接下来都是操作这个副本
$key = currentKey($arrCopy); //将获取到的值分配给$k;
$val = currentVal($arrCopy); //将获取到的值分配给$v;
next($arrCopy);//移动副本数组的指针
$arr = $arrCopy;//将副本赋值回给$arr((主要是将指针同步移动)此时仍是一个结构体)
//大括号内容
{
     $arr[$key] = $value;//这是就会发生写时复制,二者分离
}
//firt loop end

//second loop 
$key = currentKey($arrCopy); //将获取到的值分配给$k;
$val = currentVal($arrCopy); //将获取到的值分配给$v;
………
//seconde loop end
?>

 

关于对象的复制,还牵出来一个概念那就是浅复制和深复制。一般的变量(非对象)的复制都是复制出来一份新的,深浅的概念是针对对象复制,确切的说是有关引用

浅复制:就是适可而止的意思。被赋值对象的所有变量都还有与原来对象相同的值(这里是指变量内容是一样,如果有的变量是对象的话,那他是个引用(php5说的,new回来的是一个引用),那复制新的变量也是引用,所以两个变量共同指向一个变量),而所有的对其他对象的引用都仍然指向原来的对象。也就是说,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象

深复制概念:被复制的对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量(是想表达不是相同的引用了,并不是傻傻的和人家一样指向一个地方,而是根据指向的内容新建一个一样内容的对象)也就是说深复制把要复制的对象所引用的对象都复制了一遍

其实关于对象的深复制有两种方式实现:

1:利用__clone()魔术方法来完善,但不适合很多有很多引用的时候。

2:利用serialize()方法,他会把所有依赖的对象全部复制一份,等反序列化的时候,全部创建出来新的。

为什么要先理解引用呢,因为我们接下来要说的是与引用不同的另一种手法。先来看一段代码。

$a = 23;  | $a = 23;

$b = $a; | $b = &$a;

$b = 42; | $b = 42;

var_dump($a); // int(23) | var_dump($a)://int(42)

var_dump($b); // int(42) | var_dump($b);//int(42)

 

这是为什么?这就是两种不同的机制,一种是值传递(拷贝),一种是地址传递(引用)。对于php的普通变量来说,采用的而是值传递。就是一个拷贝,完全独立的一个拷贝。关于如何破除引用产生二者公用一个内容,就是使用unset销毁其中一个变量,但另一个不受影响。因为事实上,unset是切断了引用(断开了变量名和变量内容之间的绑定,这并不意味着变量内容被销毁了,可以使用变量=null 的方法来强制释放其内存)。在php引用存在中,一个变量值是否该被回收取决于引用计数。在 PHP 中,引用不仅能用在普通语句中,还能用于函数参数和返回值

函数的引用传递(参数前面加&+函数的引用返回(函数名前面加&)

如:function &foo(&$param) {

 $param = 42;

 return $param;

}

根据官网介绍,只有a = &test($b);的形式得到的才是函数的引用返回。不加&的只是普通的返回值赋值。通过a=&test()方式调用的函数 他的作用是 将return param中的param量的内存地址与a变量的内存地址 指向了同一个地方。即产生了相当于这样的效果(a=&param;) ,这个多用在对象中。

还能用于对象中,在PHP5中 对象的赋值是个引用的过程.b=new a();  c=b;  其实等效于b=new a(); c= &b;(已过时,会出E_STRICT 级别的消息)

PHP5中默认就是通过引用来调用对象 也就是说在php5中,你不需要额外添加什么东西就可到达“对象引用”的功能, 但有时你可能想建立一个对象的副本,并希望原来的对象的改变不影响到副本 . 为了这样的目的,PHP5定义了一个特殊的方法,称为__clone。

PHP 5 起new 自动返回引用,因此在此使用 =& 已经过时了并且会产生 E_STRICT 级别的消息。在php4中,对象的赋值是个拷贝过程,两个相互独立互不影响。

 

这里还要单独说一下global 引用,这是对全局变量的一个引用,用代码来表述就是,一下语句是对等的。global $var$var =& $GLOBALS["var"];这意味着,例如,unset $var 不会 unset 全局变量。  如果在一个函数内部给一个声明为 global 的变量赋于一个引用,该引用只在函数内部可见(也就是局部变量)。可以通过使用 $GLOBALS 数组避免这一点。

还有就是$this,在一个对象的方法中,$this 永远是调用它的对象的引用

 

注意一个概念 “写时复制”。我的理解就是偷懒。比如下面的代码。

$a = array('a','c'...'n');$b = $a;//这是zend会偷个懒,不同变量指向同一个内存地址。

function printArray(&$arr){ //引用传递

   print(count($arr));

}

 printArray($a);//这时zend就会真正的给b分配内容,与a区分开。

还有一种情况,没有产生写时复制。代码如下

$foo['love'] = 1; 
$bar  = &$foo['love']; 
$tipi = $foo; 
$tipi['love'] = '2'; //原样复制,但里面有引用
echo $foo['love'];

这种情况$bar变量的引用操作,将$foo['love']污染变成了引用(两者相互引用),从而Zend没有对$tipi['love']的修改产生内存的复制分离。

 

关于对象的复制,还牵出来一个概念那就是浅复制和深复制。一般的变量(非对象)的复制都是复制出来一份新的,深浅的概念是针对对象复制,确切的说是有关引用

浅复制:就是适可而止的意思。被赋值对象的所有变量都还有与原来对象相同的值(这里是指变量内容是一样,如果有的变量是对象的话,那他是个引用(php5说的,new回来的是一个引用),那复制新的变量也是引用,所以两个变量共同指向一个变量),而所有的对其他对象的引用都仍然指向原来的对象。也就是说,浅复制仅仅复制所考虑的对象,而不复制它所引用的对象

深复制概念:被复制的对象的所有的变量都含有与原来的对象相同的值,除去那些引用其他对象的变量(是想表达不是相同的引用了,并不是傻傻的和人家一样指向一个地方,而是根据指向的内容新建一个一样内容的对象)也就是说深复制把要复制的对象所引用的对象都复制了一遍

其实关于对象的深复制有两种方式实现:

1:利用__clone()魔术方法来完善,但不适合很多有很多引用的时候。

2:利用serialize()方法,他会把所有依赖的对象全部复制一份,等反序列化的时候,全部创建出来新的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值