Forum und email

Las referencias al interior del constructor

Crear referencias al interior del constructor puede llevar a resultados confusos. Esta sección tipo-tutorial le ayuda a evitar problemas.

<?php
class Foo {
    function 
Foo($nombre) {
        
// crear una referencia al interior de la matriz global $refglobal
        
global $refglobal;
        
$refglobal[] = &$this;
        
// definir el nombre al valor pasado
        
$this->definirNombre($nombre);
        
// e imprimirlo
        
$this->imprimirNombre();
    }

    function 
imprimirNombre() {
        echo 
"<br />"$this->nombre;
    }

    function 
definirNombre($nombre) {
        
$this->nombre $nombre;
    }
}
?>

Revisemos si existe una diferencia entre $bar1, que ha sido creado usando el operador de copia = y $bar2 que ha sido creado usando el operador de referencia =&...

<?php
$bar1 
= new Foo('definido en el constructor');
$bar1->imprimirNombre();
$refglobal[0]->imprimirNombre();

/* salida:
definido en el constructor
definido en el constructor
definido en el constructor */

$bar2 =& new Foo('definido en el constructor');
$bar2->imprimirNombre();
$refglobal[1]->imprimirNombre();

/* salida:
definido en el constructor
definido en el constructor
definido en el constructor */
?>

Aparentemente no hay ninguna diferencia, pero en realidad hay una bastante importante: $bar1 y $refglobal[0] _NO_ son referenciados, NO son la misma variable. Esto se debe a que "new" no devuelve una referencia por defecto, en su lugar devuelve una copia.

Note: No existe una pérdida de rendimiento (ya que desde PHP 4 se usa el conteo de referencias) al devolver copias en lugar de referencias. Al contrario, con frecuencia es mejor trabajar simplemente con copias en lugar de referencias, ya que crear referencias toma cierto tiempo mientras que crear copias prácticamente no toma nada de tiempo (a menos que ninguna de ellas sea una matriz u objeto grande y una de ellas se modifique y luego las otras subsecuentemente, entonces sería buena idea usar referencias para modificarlas todas al mismo tiempo).

Para probar lo que se dice más arriba, veamos el siguiente código.
<?php
// ahora cambiaremos el nombre. ¿qué espera que pase?
// puede que espere que tanto $bar1 como $refglobal[0] cambien sus nombres...
$bar1->definirNombre('definido desde afuera');

// como se ha mencionado antes, ese no es el caso.
$bar1->imprimirNombre();
$refglobal[0]->imprimirNombre();

/* salida:
definido desde afuera
definido en el constructor */

// veamos qué cambia entre $bar2 y $refglobal[1]
$bar2->definirNombre('definido desde afuera');

// por suerte, no solo son iguales, son la misma variable, de modo que
// $bar2->nombre y $refglobal[1]->nombre son el mismo también
$bar2->imprimirNombre();
$refglobal[1]->imprimirNombre();

/* salida:
definido desde afuera
definido desde afuera */
?>

Otro ejemplo final, intente entenderlo.

<?php
class {
    function 
A($i) {
        
$this->valor $i;
        
// intente descubrir porqué no necesitamos una referencia aquí
        
$this->= new B($this);
    }

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

    function 
echoValor() {
        echo 
"<br />","clase ",get_class($this),': ',$this->valor;
    }
}


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

    function 
echoValor() {
        echo 
"<br />","clase ",get_class($this),': ',$this->a->valor;
    }
}

// intente entender porqué usar una simple copia produciría
// un resultado no deseado en la línea marcada con *
$a =& new A(10);
$a->crearRef();

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

$a->valor 11;

$a->echoValor();
$a->b->echoValor(); // *
$a->c->echoValor();

?>

El resultado del ejemplo seria:

clase A: 10
clase B: 10
clase B: 10
clase A: 11
clase B: 11
clase B: 11