構造函數中的引用

在構造函數中建立引用可能會導致混淆的結果。本節以教學形式說明避免問題。

<?php
class Foo {
    function 
Foo($name) {
        
// 在全局陣列 $globalref 中建立一個引用
        
global $globalref;
        
$globalref[] = &$this;
        
// 將名字設定為傳遞的值
        
$this->setName($name);
        
// 並輸出之
        
$this->echoName();
    }

    function 
echoName() {
        echo 
"<br />",$this->name;
    }

    function 
setName($name) {
        
$this->name $name;
    }
}
?>

下面來檢查一下用複製運算符 = 建立的 $bar1 和用引用運算符 =& 建立的 $bar2 有沒有區別...

<?php
$bar1 
= new Foo('set in constructor');
$bar1->echoName();
$globalref[0]->echoName();

/* 輸出:
set in constructor
set in constructor
set in constructor */

$bar2 =& new Foo('set in constructor');
$bar2->echoName();
$globalref[1]->echoName();

/* 輸出:
set in constructor
set in constructor
set in constructor */
?>

顯然沒有區別,但實際上有一個非常重要的區別:$bar1$globalref[0] 並沒有被引用,它們不是同一個變量。這是因為「new」預設並不返回引用,而返回一個複製。

注: 在返回複製而不是引用中並沒有效能上的損失(因為 PHP 4 及以上版本使用了引用計數)。相反更多情況下工作於複製而不是引用上更好,因為建立引用需要一些時間而建立複製實際上不花時間(除非它們都不是大的陣列或對象,而其中之一跟著另一個變,那使用引用來同時修改它們會更聰明一些)。

要證明以上寫的,看看下面的代碼。

<?php
// 現在改個名字,你預期什麼結果?
// 你可能預期 $bar1 和 $globalref[0] 二者的名字都改了...
$bar1->setName('set from outside');

// 但如同前面說的,並不是這樣。
$bar1->echoName();
$globalref[0]->echoName();

/* 輸出為:
set from outside
set in constructor */

// 現在看看 $bar2 和 $globalref[1] 有沒有區別
$bar2->setName('set from outside');

// 幸運的是它們不但相同,根本就是同一個變量。
// 因此 $bar2->name 和 $globalref[1]->name 也是同一個變量。
$bar2->echoName();
$globalref[1]->echoName();

/* 輸出為:
set from outside
set from outside */
?>

最後給出另一個例子,試著理解它。

<?php
class {
    function 
A($i) {
        
$this->value $i;
        
// 試著想明白為什麼這裡不需要引用
        
$this->= new B($this);
    }

    function 
createRef() {
        
$this->= new B($this);
    }

    function 
echoValue() {
        echo 
"<br />","class ",get_class($this),': ',$this->value;
    }
}


class 
{
    function 
B(&$a) {
        
$this->= &$a;
    }

    function 
echoValue() {
        echo 
"<br />","class ",get_class($this),': ',$this->a->value;
    }
}

// 試著理解為什麼這裡一個簡單的複製會在下面用 *
// 標出來的行中產生預期之外的結果
$a =& new A(10);
$a->createRef();

$a->echoValue();
$a->b->echoValue();
$a->c->echoValue();

$a->value 11;

$a->echoValue();
$a->b->echoValue(); // *
$a->c->echoValue();
?>

上例將輸出:

class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11