Forum und email

Referenzen innerhalb des Konstruktors

Referenzen innerhalb des Konstruktors können zu verwirrenden Resultaten führen. Dieser Abschnitt hilft, Probleme zu vermeiden.

class Foo
{
    function Foo($name)
    {
        // eine Referenz innerhalb des globalen Arrays  $globalref erstellen
        global $globalref;
        $globalref[] = &$this;
        // setze den Namen auf den übergebenen Wert
        $this->setName($name);
        // und gib' ihn aus
        $this->echoName();
    }

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

Prüfen wir, ob zwischen $bar1, die mittels dem Operator zum Kopieren = erstellt wurde, und $bar2, die mittels dem Referenzoperator =& erstellt wurde, ein Unterschied besteht...

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

/* Ausgabe:
set in constructor
set in constructor
set in constructor */

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

/* Ausgabe:
set in constructor
set in constructor
set in constructor */

Scheinbar besteht kein Unterschied, aber tatsächlich existiert ein signifikanter: $bar1 und $globalref[0] sind NICHT referenziert, d.h. sie sind NICHT die selbe Variable. Das kommt daher, dass "new" nicht automatisch eine Referenz, sondern eine Kopie zurückgibt.

Hinweis: Das zurückgeben von Kopien anstatt von Referenzen stellt keinen Performanceverlust dar (da PHP 4 und höher Reference Counting verwendet). Andererseits ist es sehr oft besser, einfach mit Kopien anstatt mit Referenzen zu arbeiten, da die Erstellung von Referenzen etwas Zeit in Anspruch nimmt, während das Erstellen von Kopien so gut wie keine Zeit braucht (sofern keine von ihnen ein großes Array oder Objekt ist, und eines davon geändert wird und das/die andere/n nachträglich. In diesem Fall wäre es besser, Referenzen zu verwenden, um sie alle gleichzeitig zu ändern).

Um das zuvor geschriebene zu beweisen, sehen wir uns den folgenden Code an.
// nun werden wir den Namen ändern. Was erwarten Sie?
// ...dass sowohl $bar1, als auch $globalref[0] ihre Namen ändern...
$bar1->setName('set from outside');

// wie bereits zuvor erwähnt, ist dies nicht der Fall
$bar1->echoName();
$globalref[0]->echoName();

/* Ausgabe:
set from outside
set in constructor */

// lassen Sie uns den Unterschied zwischen $bar2 and $globalref[1] ansehen
$bar2->setName('set from outside');

// glücklicherweise sind sie nicht nur nicht gleich, sondern auch die selbe
// Variable; demnach sind $bar2->name und $globalref[1]->name ebenfalls gleich
$bar2->echoName();
$globalref[1]->echoName();

/* Ausgabe:
set from outside
set from outside */

Ein anderes, letztes Beispiel zum Verständnis:

class A
{
    function A($i)
    {
        $this->value = $i;
        // finden Sie heraus, warum wir hier keine Referenz benötigen
        $this->b = new B($this);
    }

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

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


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

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

// überlegen Sie, warum hier die Verwendung einer einfachen Kopie in der
// mit * markierten Zeile zu einem unerwünschten Ergebnis führen würde
$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();

/*
Ausgabe:
class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11
*/