【问题标题】:PHP Object Assignment vs CloningPHP 对象分配与克隆
【发布时间】:2013-05-29 10:32:28
【问题描述】:

我知道这在 php 文档中有介绍,但我对这个问题感到困惑。

来自 php 文档:

$instance = new SimpleClass();
$assigned   =  $instance;
$reference  =& $instance;
$instance->var = '$assigned will have this value';
$instance = null; // $instance and $reference become null
var_dump($instance);
var_dump($reference);
var_dump($assigned);
?>

上面的例子会输出:

NULL
NULL
object(SimpleClass)#1 (1) {
["var"]=>
 string(30) "$assigned will have this value"
}

好的,所以我看到$assigned 幸存原来的对象($instance)被分配给null,所以很明显$assigned 不是参考,而是$instance 的副本.

那么和

有什么区别
 $assigned = $instance 

 $assigned = clone $instance

【问题讨论】:

  • '$assigned will have this value' 不会因为你使用''
  • @Robert 在这种情况下,“$assigned”的意思是字面上的“$assigned”,而不是值替换。
  • 我今天花了几个小时处理一个棘手的错误,这是由于我不了解涉及数组和对象的赋值语句之间的极端差异。我仍然不明白为什么会有差异。这似乎是糟糕的语言设计,因为对象只是花哨的数组(具有可见性控制、固定属性列表和添加的方法)。一般来说,当我使用赋值语句时,我几乎总是想捕获右侧表达式的当前值。在我需要别名(参考)的极少数情况下,我希望在 RHS 上需要 '&' 运算符。

标签: php object


【解决方案1】:

嗯,基本上这些变量只不过是指向对象所在内存空间的指针。如果将指针值存储在另一个指针中,然后重置原始指针,则它们曾经指向的内存区域将不会发生任何事情。

【讨论】:

    【解决方案2】:
     $assigned = $instance 
    

    上面会将 $instance 分配给 $assigned,这是最基本的分配。

     $assigned = clone $instance 
    

    这是用于对象克隆。将对象 $instance 的副本分配给 $assigned。

    没有克隆,$assigned 和 $instance 具有相同的对象 id,这意味着它们指向同一个对象。

    【讨论】:

    • 但对方说so obviously $assigned isn't a reference but a copy of $instance
    【解决方案3】:

    对象是内存中的抽象数据。变量总是在内存中保存对这些数据的引用。想象$foo = new Bar 在内存某处创建Bar 的对象实例,并为其分配一些id #42,而$foo 现在持有此#42 作为对该对象的引用。将此引用分配给其他变量通过引用 或通常与任何其他值相同。许多变量可以保存此引用的副本,但都指向同一个对象。

    clone 显式创建对象本身的副本,而不仅仅是指向对象的引用。

    $foo = new Bar;   // $foo holds a reference to an instance of Bar
    $bar = $foo;      // $bar holds a copy of the reference to the instance of Bar
    $baz =& $foo;     // $baz references the same reference to the instance of Bar as $foo
    

    不要将=& 中的“引用”与对象标识符中的“引用”混淆。

    $blarg = clone $foo;  // the instance of Bar that $foo referenced was copied
                          // into a new instance of Bar and $blarg now holds a reference
                          // to that new instance
    

    【讨论】:

      【解决方案4】:

      两者的区别

       $assigned = $instance 
      

       $assigned = clone $instance
      

      是在第一种情况下,您分配一个已存在对象的引用,而在第二种情况下,您创建一个新对象并将其分配给变量。

      此外,当您使用 clone 关键字时,您可以使用魔术方法 __clone() 来更好地控制对象克隆。来自php手册:

      克隆完成后,如果定义了 __clone() 方法,则将调用新创建对象的 __clone() 方法,以允许任何需要更改的必要属性。

      来自manual

      PHP 引用是一个别名,它允许两个不同的变量写入同一个值。从 PHP 5 开始,对象变量不再包含对象本身作为值。它只包含一个对象标识符,允许对象访问者找到实际对象。当一个对象通过参数发送、返回或分配给另一个变量时,不同的变量不是别名:它们持有标识符的副本,它指向同一个对象。

      让我给你一个活生生的例子

      $dateA = new \Datetime('2017-04-04');
      
      $dateB = $dateA; // $dateB references exactly the same object as $dateA
      
      $dateB->modify('+1 day');
      
      var_dump($dateA->format('Y-m-d')); //string(10) "2017-04-05" 
      var_dump($dateB->format('Y-m-d')); //string(10) "2017-04-05"
      
      
      // $dateA is still modified by the code above so it has 2017-04-05
      $dateC = clone $dateA; // we clone $dateA so it's a new object
      
      $dateC->modify('+1 day');
      
      var_dump($dateA->format('Y-m-d')); // string(10) "2017-04-05" 
      var_dump($dateC->format('Y-m-d')); // string(10) "2017-04-06"
      

      // 日期时间的旁注我建议使用 DatetimeImmutable 而不是 Datetime

      编辑:内部类型

      // create 2 integer variables $a and $b
      
      $a = 1;
      $b = 1;
      
      // create a new $c variable and assign the *value* of $a to that variable
      $c = $a;
      
      // create a new $d variable and assign a reference to $b variable
      $d = &$b;
      
      // increment $b, $c and $d variables
      $b++; 
      $c++;
      $d++;
      
      echo $a; // 1
      echo $b; // 3
      echo $c; // 2
      echo $d; // 3
      

      因为当我们增加它的值时,$d 引用了 $b,它也会改变 $b 的值。

      字符串、int、float等内部对象的区别在于它们是按值传递的,而对象默认是通过引用传递的

      注意:您不能将克隆与内部对象一起使用。

      【讨论】:

      • 你能在你的代码中包含 &= 用例吗?那真的很有帮助。
      • @VishwasAtrey 我已经包含了它
      【解决方案5】:

      在这里保持简短和简单:

      这里的$reference 就像对象$instance 的别名。而最初$assignment$reference 指向对象$instance 的数据空间。

      $instance 指向的值发生变化时,它会随处变化。

      但是当$instance = null 时,这里我们将$instance 指向null,因为$reference 是一个别名,所以:

      $reference -> $instance -> null....

      $assignment 仍然保存指向由$instance 创建的对象的数据空间的指针,但现在$instance 不再需要相同的指针。

      【讨论】:

        【解决方案6】:

        它只是创建了一个全新的对象,它保留了被复制对象的属性。它执行深层复制:

        $assigned = $instance; 
        

        当您将一个对象从一个对象复制到另一个对象时,它会进行浅拷贝:

        $assigned = clone $instance;
        

        【讨论】:

        • $assigned = $instance; 不执行任何复制。它只是创建了另一个指向同一个对象的变量。
        【解决方案7】:

        PHP 管理对象的方式与管理其他数据类型的方式不同。字符串(或整数、布尔值、浮点数或数组)直接存储在变量中。当变量的值被赋值给另一个变量时,该值被复制1到新的变量中。

        例如:

        $x = array('a');
        $y = $x;
        // $x and $y are different and unrelated variables; they do not share anything
        $y[] = 'b';
        print_r($y);
        // Array
        // (
        //     [0] => a
        //     [1] => b
        // )
        print_r($x);
        // Array
        // (
        //     [0] => a
        // )
        

        PHP 如何处理对象分配?

        另一方面,PHP 使用唯一标识符处理对象。将对象分配给变量时,标识符存储在变量中,而不是实际对象中。

        当变量的值被赋值给另一个变量时,复制的是标识符而不是对象本身。这使得两个变量指向同一个对象。

        使用您的示例,变量$instance$assigned 的值相等,它们都包含同一对象的标识符。另一方面,$reference 是一个引用,即变量$assigned 的别名(不同的名称)。这就是为什么 $instance = null; 语句会清除变量 $reference$assigned 的内容,但不会影响变量 $instance 以及其中存储标识符的对象。

        在将$reference 设置为null 之前,可以使用$instance$assignedreference 中的任何一个来访问在示例的第一行创建的SimpleClass 对象。 F.e:

        $instance = new SimpleClass();
        $assigned = $instance;
        
        $instance->var = '$assigned will have this value';
        echo($instance->var);
        // It prints: 
        // $assigned will have this value
        
        // $assigned is also modified because it is the same object
        echo($assigned->var);
        // It prints: 
        // $assigned will have this value
        

        在文档中了解更多关于 PHP objects and references 的信息。

        $assigned = clone $instance 时会发生什么?

        clone operator 创建其操作数的副本。它创建一个新对象,并通过将原始对象的属性值分配给它们来初始化其所有属性。这意味着如果克隆对象包含对象作为属性,则这些属性通过简单赋值而不是通过克隆来复制。2

        $assigned = clone $instance;
        

        在此语句之后,$assigned 包含与 $instance 不同的值,因为它们现在存储不同对象的 ID。作为不同的对象,$instance 的变化不再影响$assigned

        $instance = new SimpleClass();
        $instance->var = '$instance has this value';
        
        $assigned = clone $instance;
        echo($assigned->var);
        // It prints: 
        // $instance has this value
        
        $assigned->var = '$assigned has a different value';
        echo($assigned->var);
        // It prints:
        // $assigned has a different value
        
        // $instance is not modified
        echo($instance->var);
        // It prints: 
        // $instance has this value
        

        1 这并不完全正确。出于优化目的,数组在被修改之前不会被复制(写时复制)。但是,这是一个实现细节,为了讨论的目的,最好考虑除对象之外的所有值,这些值在分配给新变量时会被复制。

        2 这也称为“浅”克隆。为了获得“深度”克隆(与原始对象不共享任何内容的真正副本),具有对象作为属性的克隆对象的类必须实现 __clone() 魔术方法并克隆原始对象的属性目的。另外,这些属性的类必须实现__clone()等等。

        【讨论】:

        • 值得一提的是$instance = null; echo $assigned->var;之所以还能工作,是因为$instance = null;并没有销毁对象,只是清除了存储在$instance中的指向对象的指针,所以对象仍然可以通过$assigned 访问。直到$assigned 也被清除并且不再有指向该对象的指针,它才会从内存中删除。
        【解决方案8】:

        这里是克隆和赋值的区别:

        class MyClass {
          public $color;
          public $amount;
        }
        
        $obj = new MyClass();
        $obj->color = 'red';
        $obj->amount = 5;
        $copy = clone $obj;
        $assign = $obj;
        $obj->amount = 7;
        print_r($copy);//it will print : MyClass Object ( [color] => red [amount] => 5 ) 
        print_r($assign);//it will print : MyClass Object ( [color] => red [amount] => 7 ) 
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2011-10-21
          • 1970-01-01
          • 1970-01-01
          • 2013-09-06
          • 2012-06-05
          • 2018-03-21
          • 2013-01-15
          相关资源
          最近更新 更多