【问题标题】:Why one of the two object instances pointing to the same object behaves differently upon assigning NULL to any of them?为什么指向同一个对象的两个对象实例之一在将 NULL 分配给它们中的任何一个时表现不同?
【发布时间】:2017-11-17 05:58:56
【问题描述】:

考虑下面的代码 sn-p :

<?php
  class SimpleClass {
    // property declaration
    public $var = 'a default value';

    // method declaration
    public function displayVar() {
      echo $this->var;
    }
  }

  $instance = new SimpleClass();

  $assigned   =  $instance;

  $instance->var = '$assigned will have this value';
  var_dump($instance);
  echo "<br>";
  var_dump($assigned);
?>

以上代码的输出如下:

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

我对上面的程序很了解。我理解对$instance$assigned 的任何更改都会反映在另一个中,因为它们指向同一个对象实例。

但是在将NULL 分配给两个对象实例中的任何一个时,这个原则在下面的代码中失败了。预计两个对象实例都应变为NULL,但两者之一包含NULL,另一个不包含NULL。为什么会这样?

<?php
  class SimpleClass {
    // property declaration
    public $var = 'a default value';

    // method declaration
    public function displayVar() {
      echo $this->var;
    }
  }

  $instance = new SimpleClass();

  $assigned   =  $instance;

  $instance = NULL;
  var_dump($instance);
  echo "<br>";
  var_dump($assigned);
?>

以上代码输出如下:

NULL
object(SimpleClass)#1 (1) { ["var"]=> string(15) "a default value" } 

此外,在上面的代码中,没有调用类方法 displayVar() 的地方,它与文本 'a default value' 相呼应,也没有任何构造方法存在,那么对象实例 $assignedvar 属性是如何获取的设置而不是在其中包含NULL

由于两个对象实例都指向同一个对象,为什么当NULL被分配给其中一个时它们以一种方式表现,而当字符串或一些有意义的值被分配给其中一个时它们以另一种(预期的)方式表现?

【问题讨论】:

  • 通过将对象分配给变量,它只复制该对象的“内存地址”,而不是整个对象及其内容。要复制对象及其内容,您需要使用: $assigned = clone $instance;

标签: php class object reference


【解决方案1】:

$assigned = $instance 表示您正在复制一个指针,因为每当您将一个新对象分配给一个变量时,您基本上是在对该对象进行指针引用。

因此,当您执行 $instance = NULL 时,您将 null 分配给 $instance 变量,而 $assigned 仍指向您之前创建的对象

看看这个链接http://php.net/manual/en/language.oop5.references.php

【讨论】:

  • 看第一个代码sn -p。其中,$assigned 和 $instance 的属性值都发生了变化。因此,当将 NULL 分配给其中任何一个时,它们的行为方式应该相同。为什么不同类型的值会有这样的行为差异?
  • @user2839497 在第一个代码 sn-p 中,您正在更改由两个变量 $assigned$instance 引用的对象本身。但是,当您执行 $assigned = NULL 时,您并没有使对象变为 NULL,而是使 $assigned 指的是 NULL,而对象本身在内存中仍然是安全的
  • @user2839497 没有区别。在您分配的两个变量中都获得了值。一个是var,另一个是instance
【解决方案2】:

当你创建一个对象时,PHP 会在底层创建一个 zval。它是内存中的一些空间,用于保存对象结构和数据。好的,it's somewhat more complex,但这就是本案您需要知道的全部内容。

将对象分配给$instance 意味着您将指针之类的东西分配给这个zval。然后当你执行$assigned = $instance 时,PHP 只会让$assigned 也指向这个 zval:

$instance -> your object <- $assigned
    ^                           ^
    |____________ = ____________|

因此,您实际上有两个指向同一底层数据结构的独立指针(由上面的 -&gt;&lt;- 表示)。

当您将NULL 分配给其中一个变量时,您只是清除了此特定变量指向 zval 的指针。但是另一个变量仍然指向它:

$instance = NULL;

// causes

$instance -> NULL
     ^       your object <- $assigned
     |                          ^
     |___________ != ___________|

让我们在交互式会话中试试这个(在你的 shell 中输入 php -a):

php > $a = new StdClass;
php > $a->foo = 42;
php > $b = $a;
php > var_dump($b);
object(stdClass)#1 (1) {
  ["foo"]=>
  int(42)
}
php > $a = null;
php > var_dump($b);
object(stdClass)#1 (1) {
  ["foo"]=>
  int(42)
}

这与使用实际引用不同,其中$assigned实际上指向$instance的指针:

your object <- $instance <- $assigned
                   ^            ^
                   |_____= _____|

当您在此处将$instance 设置为NULL 时,您正在删除您的对象与$instance 之间的连接,因此$assigned 也会失去它与您的对象的(间接)连接。

php > $a = new StdClass;
php > $a->foo = 42;
php > $b = &$a;
php > var_dump($b);
object(stdClass)#2 (1) {
  ["foo"]=>
  int(42)
}
php > $a = null;
php > var_dump($b);
NULL

通过执行$b = &amp;$a; $b 并不直接指向zval,而是实际上指向$a,因此当您将NULL 分配给$a 时,$b 将是您分配给$a 的任何内容和反之亦然,例如如果你之后做$b = 42$a 也将是 42:

$instance = null;
// causes
null <- $instance <- $assigned
            ^            ^
            |_____= _____|

因此,即使您设置了$instance = null,您仍然保持从$assigned$instance 的引用。要完全删除/取消链接$a$b 之间的关联,您需要使用unset

【讨论】:

  • 感谢您提供完整且有意义的解释。最后一句“$b 将是您将 $b 分配给的任何内容”您的意思是在删除变量 $a 后,我可以为变量 $b 分配任何值,因为在删除 $a 后它已变为空闲/空/NULL (这是参考)?
  • @user2839497 抱歉,我打错了。添加了说明。
  • 很抱歉再次打扰您。但这是我的最后一个疑问。通过“删除 $a”,您是在说 unset($a) 还是 $a = NULL 或其他什么?
  • @user 再次更新。希望这可以澄清它。我建议使用交互式控制台来解决它。您可以通过 shell 上的php -a 启动它。
  • @PHPLover 你在里面输入“exit”
猜你喜欢
  • 1970-01-01
  • 2020-01-22
  • 2015-12-15
  • 2015-06-28
  • 2021-04-17
  • 1970-01-01
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
相关资源
最近更新 更多