2

我试图理解 PHP 参考,但在使用链式参考时发现了一个问题。

class A
{
    public $val;

    public function __construct($val)
    {
        $this->val = $val;
    }
}

$values = array(
    'a' => new A('a'),
    'b' => new A('b'),
    'c' => new A('c')
);

$values['a'] = &$values['b'];
$values['b'] = &$values['c'];

返回:

array(
'a' => new A('b'),
'b' => new A('c'),
'c' => new A('c')
)

为什么键'a'的对象A中的'val'是'b'?我预计该值将是“c”。谢谢

4

3 回答 3

1

$values['b']分配给 时不存在$values['a']。如果您更改订单,它会按照您的描述工作:

~$ php -a
php > $values = array();
php > $values['c'] = 1;
php > $values['b'] = &$values['c'];
php > $values['a'] = &$values['b'];
php > print_r($values);
Array
(
    [c] => 1
    [b] => 1
    [a] => 1
)
于 2013-09-28T13:28:25.543 回答
1

尽量避免引用,它们可能会给您带来难以调试的麻烦。此外,对象在幕后被引用;这意味着:

$a1 = new A('a');
$a2 = $a1;

类似于:_

$b1 = new A('a');
$b2 = &$b1;

顺便说一句:

$a2->val = 1; // $a1->val is now equal to 1, because $a1 and $a2 are pointing 
              // to the same instance
$b2->val = 1; // $b1->val is now equal to 1, because $b2 points to $b1

但是有一个细微的区别:

$a1 = 1; // $a2 still points to A object
$b1 = 1; // $b2 still points to $a1 which points to number 1,
         // therefore $b2 == 1

此外,它与数组的工作方式略有不同,因为数组赋值总是涉及值复制。

如果您想了解示例中发生的情况,让我们看一下:

所以你的原始数组是这样的:

$values = array(
    'a' => new A('a'),
    'b' => new A('b'),
    'c' => new A('c')
);

让我们一步一步地看看那里发生了什么:

$values['a'] = &$values['b']; // $values['a'] is now reference to new A('b')
                              // that means your original new A('a') is now
                              // lost

$values['b'] = &$values['c']; // $values['b'] is now reference to new A('c')
                              // stored under 'c' key, that means $values['b']
                              // is now equal to $values['c'] ; note that this is 
                              // different than $b2 = &$b1; from the above example
                              // since we use an array and not bare variables
                              // the $values['a'] points to value stored under 
                              // the 'b' key, but we replace the 'b' key value 
                              // as opposed to giving it a new value; 
                              // "Array assignment always involves value copying."

// So you ended up with this result:
array(
  'a' => new A('b'),
  'b' => new A('c'), // this is actually reference to 'c', just wrote new A() 
                     // to keep this part consistent with your question's
                     // formatting
  'c' => new A('c') 
)
于 2013-09-28T13:44:47.800 回答
0

注意:这个答案是对最初提出的问题的回答。新问题有些不同,尽管在这种情况下使用对象不会改变基本行为。

PHP 引用不能被“链接”——=&操作符不像指针引用/取消引用机制,它将两个(或更多)变量绑定在一起。但重要的是,两个变量名都不比另一个更“真实”——它们都是或全部)对无名值的引用。

例如,$foo =& $bar获取指向的值$bar指向$foo该值。如果您随后分配给任一变量(例如$foo = 42;$bar = 42;),则该值将被更新。一些动作,例如unset($bar)作用于变量,而不是,所以它们只影响那个特定的变量,而不是引用的值。

您可以通过运行以下命令来查看实际情况:$foo = 42; $bar =& $foo; unset($foo); echo $bar;请注意,这$foo是该值的“原始”名称,但即使未设置,该值仍然存在。然后,您可以将变量重$foo用于完全不同的值,而不会受到$bar任何影响。

如果您获取一个已经是引用的变量,并将其作为对其他内容的引用,这就像取消设置引用并重用变量名来设置一个新的。因此该不受影响,并且先前引用的变量名称变得独立。

在您的代码中,您可以这样做:

// 1: Make $values['a'] point to the value currently pointed at by $values['b']
$values['a'] =& $values['b']; 
// 2: Make $values['b'] point to the value currently pointed at by $values['c']
//    the previous value is now only pointed to by $values['a']
$values['b'] =& $values['c']; 
// 3: Set the value shared by $values['b'] and $values['c'] to 1
$values['c'] = 1; 

(作为技术细节,我在这里所说的“值”是 PHP 内部结构中的一个结构,称为zval.)

编辑更新的问题:

对象似乎使这更复杂,因为它们具有额外的间接级别,其工作方式几乎相同:$foo = new A('a'); $bar = $foo;仍然创建两个zvals),但这些值都是指向同一个对象的指针。

在您的情况下,这没有什么区别,因为您仍在使用引用分配 ( =&) - 如果没有,您将得到相同的结果:

// 1: Make the value of $values['a'] point to the object currently pointed at by the value of $values['b']
$values['a'] = $values['b']; 
// 2: Make the value of $values['b'] point to the object currently pointed at by the value of $values['c']
//    the previous object is now only pointed to by the value of $values['a']
$values['b'] = $values['c']; 

我们现在可以更改三件事:变量、它指向的值和指向的对象

// Change the object: updates the object shared by $values['b'] and $values['c']
$values['c']->value = 42;
// Change the value: will only update $values['b'] if =& was used to tie them together
$values['c'] = 42; 
// Change the variable: will never affect $values['b']
unset($values['c']);
于 2013-09-28T13:39:16.577 回答