Forum und email

References μέσα σε constructorr

Η δημιουργία references μέσα σε έναν constructor μπορεί να οδηγήσει σε μπερδεμένα αποτελέσματα. Αυτό το τμήμα του εγχειριδίου θα σας βοηθήσει να αποφύγετε τέτοια προβλήματα.

<?php
class Foo
{
    function 
Foo($name)
    {
        
// create a reference inside the global array $globalref
        
global $globalref;
        
$globalref[] = &$this;
        
// set name to passed value
        
$this->setName($name);
        
// and put it out
        
$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();

/* output:
set in constructor
set in constructor
set in constructor */

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

/* output:
set in constructor
set in constructor
set in constructor */
?>

Με την πρώτη ματιά δεν υπάρχει διαφορά, αλλά στην πραγματικότητα υπάρχει μια πολύ σημαντική: η $bar1 και η $globalref[0] ΔΕΝ_αλληλοαναφέρονται, ΔΕΝ είναι η ίδια μεταβλητή. Αυτό συμβαίνει γιατί η "new" δεν επιστρέψει μια αναφορά πάντα, αλλά αντίθετα επιστρέφει ένα αντίγραφο.

Note: Δεν υπάρχει χάσιμο στην απόδοση (αφού η PHP 4 και μετά χρησιμοποιούν μετρητή αναφοράς) επιστρέφοντας αντίγραφα αντί για αναφορές. Αντιθέτως είναι συχνά καλύτερο να δουλεύουμε με αντίγραφα αντί για αναφορές, επειδή η δημιουργία αναφορών παίρνει κάποιο χρόνο ενώ η δημιουργία αντίγραφων ιδεατά δε θέλει καθόλου χρόνο (εκτός και αν ένα από αυτά είναι ένας μεγάλος πίνακας ή αντικείμενο και ένα από αυτά αλλάζει καθώς και τα άλλα στη συνέχεια, τότε θα ήταν σοφότερο να χρησιμοποιήσουμε αναφορές για να τα αλλάξουμε όλα συγχρόνως).

Για να αποδείξουμε ότι μόλις είπαμε, ας δούμε τον παρακάτω κώδικα.
<?php
// now we will change the name. what do you expect?
// you could expect that both $bar1 and $globalref[0] change their names...
$bar1->setName('set from outside');

// as mentioned before this is not the case.
$bar1->echoName();
$globalref[0]->echoName();

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

// let us see what is different with $bar2 and $globalref[1]
$bar2->setName('set from outside');

// luckily they are not only equal, they are the same variable
// thus $bar2->name and $globalref[1]->name are the same too
$bar2->echoName();
$globalref[1]->echoName();

/* output:
set from outside
set from outside */
?>

Ακόμη ένα τελικό παράδειγμα, προσπαθήστε να το κατανοήσετε.

<?php
class A
{
    function 
A($i)
    {
        
$this->value $i;
        
// try to figure out why we do not need a reference here
        
$this->= new B($this);
    }

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

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


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

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

// try to undestand why using a simple copy here would yield
// in an undesired result in the *-marked line
$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();

/*
output:
class A: 10
class B: 10
class B: 10
class A: 11
class B: 11
class B: 11
*/
?>