PHP对象复制奇怪问题

码强
  • 26

PHP对象复制奇怪问题

上代码

<?php

class Container
{
     public $data;
     public $index;
     public function __construct($data = null, $index = null)
     { 
        $this->data = $data;
         $this->index = $index;
     }
 }
 
class Nihao
{
     public $head;
     public $tail;
     public function __construct()
     { 
        $this->head = new Container();
        $this->tail = $this->head;
     }
     public function enqueue($data)
     { 
        $node = new Container($data);
        //第一次调用的时候我可以理解,PHP对象复制是浅复制
        //但是当第二次调用该方法时,理论上已经被赋值为$node了.
        $this->tail->index = $node;
        $this->tail = $node;
     }
 }
 
$nihao = new Nihao();
echo json_encode($nihao).PHP_EOL;
$nihao->enqueue(0);
echo json_encode($nihao).PHP_EOL;
$nihao->enqueue(1);
echo json_encode($nihao).PHP_EOL;

//{"head":{"data":null,"index":null},"tail":{"data":null,"index":null}}
//{"head":{"data":null,"index":{"data":0,"index":null}},"tail":{"data":0,"index":null}}
//{"head":{"data":null,"index":{"data":0,"index":{"data":1,"index":null}}},"tail":{"data":1,"index":null}}

问题:

我知道PHP的对象复制是浅复制,两个成员对象head和tail指向的是同一个对象,相互影响.

  1. 在第一次调用enqueue(0)的时候已经将tail赋值为一个全新的Container()对象,为什么这一步没有影响到head的值?
  2. 在第二次调用enqueue(1)的时候,退一万步讲,这时候tail已经在上次调用,也就是enqueue(1)的时候被修改了,为什么这次还可以影响到head?
  3. 而且为什么$this->tail->index = $node; 会自定把$node放到head的最后最内层?
回复
阅读 1.5k
2 个回答
猫之良品
  • 2.5k

浅复制就是引用赋值,用C语言解释就相当于指针,变量的值其实是对象的内存地址而非对象本身。

// head指向了Container对象的内存地址
$this->head = new Container();
// tail指向了head所指向的Container对象的内存地址,所以还是那个Container对象,你误以为tail指向的是head的内存地址,但是head是引用变量
$this->tail = $this->head;

在正式的编码中应该避免重复给成员赋引用值,一个成员应该始终指向同一个对象,也要避免多个成员变量指向同一个引用值。

class Nihao {
     public $head;
     public $tail;
     public function __construct() { 
        // 内存开辟一个空间(用于存储实例化后的Container对象A) 地址为 A;
        // 内存开辟一个空间(存储变量head) 值为 A(即变量head直向实例化后的Container对象A)
        $this->head = new Container();
        // 内存开辟一个空间(存储变量tail) 值为 A(即变量tail直向实例化后的Container对象A, 这里存储的不是head的地址,因为head是一个引用类型的变量,php在实现时不会套娃)
        $this->tail = $this->head;
     }
     public function enqueue($data) { 
        // 内存开辟一个空间(用于存储实例化后的Container对象B) 地址为 B;
        $node = new Container($data);
        // 此时$this->tail指向地址A,所有操作的是Container对象(A)
        $this->tail->index = $node;
        // 将Container对象(B)的地址B存在tail变量的内存空间中(即tail此时指向Container对象(B))
        $this->tail = $node;
     }
 }
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏