bangers has asked for the wisdom of the Perl Monks concerning the following question:

This is an interesting gottya that made me lose a day's development effort amd think I was going mad to boot. I have two objects declared from one class. One of the objects redefines a method e.g.:
#!/usr/local/bin/perl -w use strict; my $class1 = shortTest->new('One'); my $class2 = shortTest->new('Two'); $class1->getName(); $class2->getName(); $class1->updateName(); $class1->getName(); $class2->getName(); package shortTest; use strict; 1; sub new { my $class = shift; my ( $name ) = @_; my $self = {}; bless $self, $class; $self->{name} = $name; *getName = \&showName; return $self; } sub showName { my $self = shift; print "$self->{name}:In 1st cut\n"; } sub updateName { my $self = shift; *getName = \&showName2; } sub showName2 { my $self = shift; print "$self->{name}:In 2nd cut\n"; }
Having thought about it, I'm not sure this isn't "expected behaviour" – except it wasn't expected by me through hours of debugging :( I've had experience in OO languages (large amounts of Delphi plus some C++ and Java) but I didn’t mess about with the vtable in them very much. I had imagined that each object had it’s own vtable. Clearly that is not the case – or is someone going to tell me that I'm even dimmer than I already feel?

Replies are listed 'Best First'.
Re: Redefining methods in non-singletons nearly drove me mad today
by ikegami (Patriarch) on Nov 17, 2005 at 19:24 UTC

    Virtual Method Table (VMT) are per-class, not per-object. But then again, Perl doesn't use VMTs.

    C++ knows which method to call by storing the object's class's VMT in the object.
    Perl knows which method to call by storing the object's class's name in the object.

    What you are manipulating is Perl's symbol table. Adding functions to a package will affect all objects of that class.

    It is possible to do what you want with overload magic (I think), or by using less obvious syntax. Here's a couple of quick and dirty ways:

    sub new { my ($class, $name) = @_; my $self = bless({ name => $name, getName => \&showName, }, $class); return $self; } $self->{getName}->();
    or
    sub new { my ($class, $name) = @_; my $self = bless({ name => $name, getName => 'showName', }, $class); return $self; } sub getName { my $self = $_[0]; my $method = $self->{getName}; $self->$method(@_); } $self->getName();
Re: Redefining methods in non-singletons nearly drove me mad today
by gaal (Parson) on Nov 17, 2005 at 19:21 UTC
    No, in Perl 5 named code belongs to a package. When you modify *getName you are writing to shortTest::getName, so all calls to getName now end up at showName2.

    If you need this kind of thing though, you could have method dispatch go through anoymous coderefs in instance data. Method calls would need to be spelled differently; like $obj1->{getName}->() . I'm sure there are more clever ways of doing this though.

Re: Redefining methods in non-singletons nearly drove me mad today
by friedo (Prior) on Nov 17, 2005 at 19:34 UTC
    If you really want to redefine methods on a per-object basis, one way to do it is to construct a new class for each object which inherrits from your base class. For example:

    package shortTest; use strict; 1; sub new { my $class = shift; my ( $name ) = @_; my $self = {}; my $new_class = $class . '::' . $self; { no strict 'refs'; @{ $new_class . '::' . 'ISA' } = ( $class ); *{ $new_class . '::' . 'getName' } = \&showName; } bless $self, $new_class; $self->{name} = $name; return $self; } sub updateName { my $self = shift; my $class = ref $self; no strict 'refs'; *{ $class . '::' . 'getName' } = \&showName2; }
    Update: Added no strict 'refs'