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

Should you call a parent constructor explicitly like below - or does the use base take care of this?
package Parent; sub new { my ($class, $arg1, $arg2) = @_; my $self = { arg1 => $arg1, arg2 => $arg2 }; bless $self, $class; return $self; } return 1; package Child; use base qw(Parent); sub new { my ($class, $arg1, $arg2, $arg3) = @_; my $self = Parent->new($arg1, $arg2); $self->{arg3} => $arg3; bless $self, $class; return $self; } return 1;
Also - is there a difference between
use base('Parent');
and
use Exporter; use vars qw(@ISA); @ISA = qw(Parent);

Replies are listed 'Best First'.
Re: Inheritance - calling parent constructor
by ikegami (Patriarch) on Dec 29, 2009 at 18:12 UTC

    or does the use base take care of this?

    base does not prevent you from overriding the parent's constructor. Nor does it prevent you from doing something silly like blessing the object twice. Fix:
    package Child; ... sub new { my ($class, $arg1, $arg2, $arg3) = @_; my $self = $class->SUPER::new($arg1, $arg2); $self->{arg3} => $arg3; return $self; } ...

    Also - is there a difference between

    Yes:

    • base doesn't load Exporter.
    • base will load Parent if it's not loaded (or silently fail).
    • base sets @ISA at compile.
    • base doesn't disable strictures for @ISA.
Re: Inheritance - calling parent constructor
by kennethk (Abbot) on Dec 29, 2009 at 17:26 UTC
    Assuming you are rolling your own constructor, as opposed to using an existing OO framework like Moose or Mouse, you should not explicitly invoke your parent class. Rather, as described in the Overridden Methods section of perltoot, you should invoke the pseudo class SUPER, as in:

    package Child; use base qw(Parent); sub new { my ($class, $arg1, $arg2, $arg3) = @_; #my $self = SUPER->new($arg1, $arg2); my $self = $class->SUPER::new($arg1, $arg2); #$self->{arg3} => $arg3; $self->{arg3} = $arg3; #bless $self, $class; return $self; } return 1;

    You do need to explicitly invoke the SUPER class, since you are overriding the local new method, and Perl does not a priori know what you've decided to call your constructor. If you were to just say "new", it would call the constructor recursively.

    Regarding your second question, as documented in base, use base 'Parent'; will perform the functions intended by the use Exporter;... code, though not following the explicitly same approaches.

    Update: Should have tested before posting. Corrected code above (left original commmented) and fixed an OP typo for argument assignment.

      Wrong syntax. That will attempt to call new in package SUPER. And you forgot to remove the bless that's now useless. See my reply to the OP for the correction.

        Wrong syntax. That will attempt to call new in package SUPER.

        - That's what I thought - i got the correct syntax from the link you had - thanks

        And you forgot to remove the bless that's now useless.

        - Could you elaborate here? I thought I have read that re-blessing is bad and was searching for best practices on re-blessing without much luck.
      thanks for the info - that works for me. I wonder the difference between using class name and SUPER? Is it OK to not have a constructor in the child object? This seems to work.
        The difference between hard-coding Parent-> as opposed to invoking SUPER is relevant when you have multiple levels of inheritance. If you created a Grandchild, a method using SUPER would climb the @ISA tree to resolve new, whereas hard-coding Parent-> will go straight there. It's also useful for code reuse, since SUPER is not package specific. For example, if you had a series of validation routines, you could open sub validate { with my $self = shift; $self->SUPER::validate(); and it would crawl the inheritance tree correctly without refactoring and the associated fagility.

        You would only need to define a constructor in the child object if it requires additional functionality - in this case, because Child has an additional property.

        If you are interested in all this or rolling your own OO, a read read through of perlboot and perltoot is merited.

        You could use
        my $self = $class->Parent::new(...);
        instead of
        my $self = $class->SUPER::new(...);

        And you might even have to in some weird situations (although use mro 'c3'; would probably be a better fix).

        But using Parent is redundant. You already specified it's the parent class. To use it here is to needlessly hardcode a value. Nothing good can come of that.

Re: Inheritance - calling parent constructor
by almut (Canon) on Dec 29, 2009 at 18:33 UTC
    use Exporter; use vars qw(@ISA); @ISA = qw(Parent);

    Did you actually mean to say use Exporter, or rather use Parent?   Just in case it hasn't become quite clear from the other replies, there is no reason to load Exporter if all you want is inheritance (except if you want to inherit from Exporter itself, of course).

      Did you actually mean to say use Exporter, or rather use Parent? Just in case it hasn't become quite clear from the other replies, there is no reason to load Exporter if all you want is inheritance (except if you want to inherit from Exporter itself, of course).

      - No, I thought you had to use Exporter to use @ISA. Thanks for the correction. i went with use base as it seems like a clearer solution.
        I prefer parent over base since it doesn't hide errors, but I don't see the point of using a module to replace
        use Foo; our @ISA = 'Foo';

        It's only one line longer, and I don't buy that the module is any clearer.