摘自:http://php.net/manual/en/language.oop5.references.php
通过上面的这段描述,我们可以知道以下几个关键点:
- 从php 5 开始,一个对象变了不再保存对象自己本身,而是保存的an object identifier,类比于指针。通过object identifier可以访问到实际的对象。并且在对象的参数传递、返回值、赋值中,接收这些操作的变量得到的是object identifier,而不是引用(或者说别名)。
- a php reference 是别名,类似于linux的软连接。
也就是说正常情况下,对象的传递是传递的object identifier。而在加了&标志时,传递的才是引用(或者说是别名)。
进一步理解,引用和object identifier有什么区别分?
构造类如下:
1 <?php 2 class A{ 3 public $a; 4 public $b; 5 public function __construct($a,$b){ 6 $this->a=$a; 7 $this->b=$b; 8 } 9 public function printContent(){ 10 echo "a=$this->a b=$this->b\n"; 11 } 12 }
进行如下操作:
1 $aa = new A(1,2); 2 $bb = $aa; //传递的是object identifier 3 $cc = &$aa; //$cc是对象aa的别名
4 $aa = new A(55,1); 5 echo '打印对象aa的内容'."\n"; 6 $aa->printContent(); //a=55 b=1 7 echo '打印对象bb的内容'."\n"; 8 $bb->printContent(); //a=1 b=2 9 echo '打印对象cc的内容'."\n"; 10 $cc->printContent(); //a=55 b=1
由上面的输出可知,当$aa的指向的内容发生变化时,因为$cc是$aa的别名,所以跟随变化。而$bb只是保存了在执行第二行语句时和$aa相同的object identifier,在执行第四行语句后,$aa保存的object identifier变成了新的对象A(55,1)的identifier,所以$bb不受影响。
进一步,查看参数传递过程中引用和identifier的差异:
定义以下两个方法:
1 function change($tmp){ 2 $tmp = new A(2,3); 3 return $tmp; 4 } 5 function changeByRef(&$tmp){ 6 $tmp = new A(4,5); 7 return $tmp; 8 }
执行如下操作:
1 $aa = new A(1,2); 2 echo '初始化时打印对象aa'."\n"; 3 $aa->printContent(); //a=1 b=2 4 5 $bb = change($aa); 6 echo '调用change方法后打印对象aa'."\n"; 7 $aa->printContent(); //a=1 b=2 8 echo '调用change方法后打印返回的对象bb'."\n"; 9 $bb->printContent(); //a=2 b=3 10 11 12 $bb = changeByRef($aa); 13 echo '调用changeByRef方法后打印对象aa'."\n"; 14 $aa->printContent(); //a=4 b=5 15 echo '调用change方法后打印返回的对象bb'."\n"; 16 $bb->printContent(); //a=4 b=5
可见,当传递的是object identifier时,改变方法的参数的object identifier不会影响到被传递的对象(即$aa)。而传递的是别名时则会影响到。
需要注意的是:不论传递的是对象的object identifier或则引用,当在方法中修改对象的属性时,因为实际上修改地是相同的object,因此会影响到被传递的对象或者被引用的对象。
更多的对象与引用的实例说明可以参考以下代码:
摘自:http://php.net/manual/en/language.oop5.references.php
<?php class Foo { private static $used; private $id; public function __construct() { $id = $used++; } public function __clone() { $id = $used++; } } $a = new Foo; // $a is a pointer pointing to Foo object 0 $b = $a; // $b is a pointer pointing to Foo object 0, however, $b is a copy of $a $c = &$a; // $c and $a are now references of a pointer pointing to Foo object 0 $a = new Foo; // $a and $c are now references of a pointer pointing to Foo object 1, $b is still a pointer pointing to Foo object 0 unset($a); // A reference with reference count 1 is automatically converted back to a value. Now $c is a pointer to Foo object 1 $a = &$b; // $a and $b are now references of a pointer pointing to Foo object 0 $a = NULL; // $a and $b now become a reference to NULL. Foo object 0 can be garbage collected now unset($b); // $b no longer exists and $a is now NULL $a = clone $c; // $a is now a pointer to Foo object 2, $c remains a pointer to Foo object 1 unset($c); // Foo object 1 can be garbage collected now. $c = $a; // $c and $a are pointers pointing to Foo object 2 unset($a); // Foo object 2 is still pointed by $c $a = &$c; // Foo object 2 has 1 pointers pointing to it only, that pointer has 2 references: $a and $c; const ABC = TRUE; if(ABC) { $a = NULL; // Foo object 2 can be garbage collected now because $a and $c are now a reference to the same NULL value } else { unset($a); // Foo object 2 is still pointed to $c }