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

Esteemed monks,

I have an OO Perl question ... well 2 actually!

My first question concerns inheritance, specifically @ISA. The code provided, works when package Employee.pm inherits from package Person.pm via:

use base qw( Person );

However when I change this to:

our @ISA = qw( Person );

I get the following error:

Can't locate object method "new" via package "Employee" at person.pl line 7.

I am not quite sure I understand why this happens and would like an explanation. If the class Person.pm can only be inherited by "use base" then that partially defeats the purpose of the encapsulation provided by inside-out classes (as it can't be inherited by saying "our @ISA = qw( Class )").

My next question concerns destruction. I'm aware that Perl calls the sub DESTROY at the end of an objects life. In the example code the Employee::DESTROY method needs to call the SUPER::DESTROY method to take care of the cleaning up required there. I noted that this does not appear to happen otherwise.

That is, when I call SUPER::DESTROY I get the output:

Hi! My name is Nelo and my job is none of your business! Destroying Employee=SCALAR(0x188f4c4) Deleted job ... Destroying Employee=SCALAR(0x188f4c4) Deleted name ... Hi! My name is Amelie and my job is none of your business! Destroying Employee=SCALAR(0x22606c) Deleted job ... Destroying Employee=SCALAR(0x22606c) Deleted name ... Hi! My name is Wole and my job is none of your business! Destroying Employee=SCALAR(0x183296c) Deleted job ... Destroying Employee=SCALAR(0x183296c) Deleted name ... Hi! My name is Hacer and my job is none of your business! Destroying Employee=SCALAR(0x188f4c4) Deleted job ... Destroying Employee=SCALAR(0x188f4c4) Deleted name ...
And when I don't I get:

Hi! My name is Nelo and my job is none of your business! Destroying Employee=SCALAR(0x188f4c4) Deleted job ... Hi! My name is Amelie and my job is none of your business! Destroying Employee=SCALAR(0x22606c) Deleted job ... Hi! My name is Wole and my job is none of your business! Destroying Employee=SCALAR(0x183296c) Deleted job ... Hi! My name is Hacer and my job is none of your business! Destroying Employee=SCALAR(0x188f4c4) Deleted job ...
My question here is "is my assumption correct" and do I need to explicitly call the SUPER::DESTROY in my child class or does Perl really get rid of the attributes defined in the parent class?

Here is the code in full with comments denoting the lines concerned:

#!/usr/bin/perl -w use strict; package main; { for my $name ( qw( nelo amelie wole hacer ) ) { my $person = Employee->new(name=>$name, job=>"none of your bus +iness!"); print "\n", 'Hi! My name is ', ucfirst $person->get_name, ' a +nd my job is ', $person->get_job, "\n"; } } 1; package Person; { use Carp qw( &carp &croak ); use Scalar::Util qw( &refaddr ); # object's attributes my (%name_of); # construct a new object sub new { my ($class, %args) = @_; ref $class and croak "Incorrect Usage: $class->new(%args)"; my $self = bless \do{my $scalar}, $class; $self->_init(%args); return $self; } # initialise the new object's attributes sub _init { my ($self, %args) = @_; ref $self or croak "Incorrect Usage: $self->_init(%args)"; # set values with reasonable defaults $self->set_name( $args{name} || 'unknown' ); } # object accessor methods sub set_name { my ($self, $name) = @_; ref $self or croak "Incorrect Usage: $self->set_name"; $name or croak "No argument provided to set_name"; $name_of{refaddr $self} = $name; } sub get_name { my ($self) = @_; ref $self or croak "Incorrect Usage: $self->get_name"; @_ == 1 or carp "No arguments required by get_name"; return $name_of{refaddr $self}; } # class accessor methods # private (internal) methods # utility (external) methods # destroy the object sub DESTROY { my ($self) = @_; print "Destroying $self\n"; delete $name_of{refaddr $self} and print "Deleted name ...\n"; } } 1; package Employee; { use Carp qw( &carp &croak ); use Scalar::Util qw( &refaddr ); # inherits from ... use base qw( Person ); # works with this :) #~ our @ISA = qw( Person ); # breaks with this :( # object's attributes my (%job_of); # construct new object (inherited) # initialise the new object's attributes sub _init { my ($self, %args) = @_; ref $self or croak "Incorrect Usage: $self->_init(%args)"; # set values with reasonable defaults $self->set_job( $args{job} || 'unknown' ); # initialise the parent class attributes $self->SUPER::_init(%args); } # object accessor methods sub set_job { my ($self, $job) = @_; ref $self or croak "Incorrect Usage: $self->set_job"; $job or croak "No argument provided to set_job"; $job_of{refaddr $self} = $job; } sub get_job { my ($self) = @_; ref $self or croak "Incorrect Usage: $self->get_job"; @_ == 1 or carp "No arguments required by get_job"; return $job_of{refaddr $self}; } # class accessor methods # private (internal) methods # utility (external) methods # destroy the object sub DESTROY { my ($self) = @_; print "Destroying $self\n"; delete $job_of{refaddr $self} and print "Deleted job ...\n"; # call parent destructor $self->SUPER::DESTROY; # comment out and "name" hangs aroun +d ... i think :) } } 1;

Any help is appreciated :)

Replies are listed 'Best First'.
Re: inside-out class and "our @ISA"
by chromatic (Archbishop) on Aug 02, 2007 at 16:50 UTC

    The important difference here between using base and manipulating @INC directly is that base attempts to load the named module. If you don't already use Person; elsewhere in your code, you have to do so within the Employee package if you manipulate @INC yourself. That's one reason I prefer to use base myself.

    You do have to make a SUPER call within your destructor; Perl's default behavior for method overriding is override only, not override and redispatch.

    (One nit is that your use of ref allows non-blessed references. To be stricter, you can use blessed() from Scalar::Util.)

      So in short:

      * use base for inheritance
      * better to use Scalar::Util::blessed() than ref()
      * and HAVE to call $self->SUPER::DESTROY for destruction

      Thanks for the advice. I find that when I place Person.pm and Employee.pm in separate files I get the desired results ;)