La Sobrecarga y el Constructor de copia

La sobrecarga de operadores da lugar a confusión en ocasiones, supongamos por ejemplo que $d y $t son dos Math::BigFloat. Una asignación como:
$v = $d/$t;
trata a $d y $t como valores en vez de como las referencias que son a los correspondientes objetos. Consideremos una asignación como:
$v1 = $v;
La asignación hace que $v1 y $v sean una referencia al mismo objeto. Si posteriormente incrementamos $v1:
$v1++;
tendremos un efecto lateral, ya que $v se ve incrementado. Este efecto es, cuando menos, inconsistente con el uso normal de las operadores sobrecargados, en el sentido de que estos ''trabajan'' los referentes (objetos) y la asignación ''trabaja'' con las referencias.

Los operadores que cambian el valor de sus operandos se llaman ''mutantes''. El módulo overload proporciona un medio para interceptar a los mutantes y clonar los objetos que están siendo mutados de manera que la mutación sólo afecte a la copia del objeto. Esta intercepción se hace sobrecargando la operador engañosamente denominado =

package Math::BigFloat;

sub copy { Math::BigFloat->new(${$_[0]}) }

use overload
      ...
      '++' => "incr",
      '='  => "copy";
Esta sobrecarga no cambia la conducta del operador de asignación. La rutina copy es llamada antes que la subrutina asociada con cualquier mutante. Asi pues, el código:

$v1++;

Se traduce por:

$v1 = $v1->copy(undef,"");
$v1->incr(undef,"");

Ejercicio 6.9.1   Considere el siguiente programa:
$ cat -n ./fractions2.pl
1  #!/usr/local/bin/perl5.8.0  -w
2  use Number::Fraction ':constants';
3
4  my $t = '1/2';
5  my $x = $t;
6  $t += '1/2';
7  print "t=$t, ref(t)=",ref($t),"\n";
8  print "x=$x, ref(x)=",ref($x),"\n";
Al ejecutarlo, produce la siguiente salida:
$ ./fractions2.pl
t=1/1, ref(t)=Number::Fraction
x=1/2, ref(x)=Number::Fraction
¿Porqué la asignación de la línea 5 ($x = $t) no parece comportarse como una asignación de referencias como se señaló en la sección anterior?. La modificación de $t no produce un efecto lateral sobre $x. Nótese que el operador = no ha sido sobrecargado en Number::Fraction. Puede comprobarlo en el listado de la cabecera de Number::Fraction que aparece en la sección 6.9.3 pagina [*].

Para ayudar a entender lo que esta ocurriendo, aqui tiene el código del método add que sobrecarga el operador +

sub add {
  my ($l, $r, $rev) = @_;

  if (ref $r) {
    if (UNIVERSAL::isa($r, ref $l)) { # Ambos heredan de ref $l
      return (ref $l)->new( # Puede que 'new' hay sido sobreescrito
                           $l->{num} * $r->{den} + $r->{num} * $l->{den},
			   $r->{den} * $l->{den}
                          );
    } else {
      croak "Can't add a ", ref $l, " to a ", ref $r;
    }
  } else {
    if ($r =~ /^[-+]?\d+$/) { # $r es un número entero
      return $l + (ref $l)->new($r, 1);
    } else { # r es un double: convertimos
      return $l->to_num + $r;
    }
  }
}

Casiano Rodríguez León
Licencia de Creative Commons
Principios de Programación Imperativa, Funcional y Orientada a Objetos Una Introducción en Perl/Una Introducción a Perl
por Casiano Rodríguez León is licensed under a Creative Commons Reconocimiento 3.0 Unported License.

Permissions beyond the scope of this license may be available at http://campusvirtual.ull.es/ocw/course/view.php?id=43.
2012-06-19