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

Dear Monks,

According to "Intermediate Perl 2nd":

1) DESTROY is like other method calls: Perl starts at the class of the object and works its way up the inheritance hierarchy until it finds a suitable method. However, unlike other method calls, there's no error if Perl doesn't find a suitable method. (p. 140)

2) So, the rule is:
Always include a call to $self->SUPER::DESTROY in our destructors (even if we don't yet have any base/parent classes). (p. 145)

However I get an error when I run this code:

use strict; use warnings; use 5.010; {package B; sub new { my $class = shift; bless {}, $class; } sub DESTROY { my $self = shift; say "destroy"; $self->SUPER::DESTROY; } } --output:-- (in cleanup) Can't locate object method "DESTROY" via package "B" at C +:\Users\Me\Documents\perl2.pl line 17. destroy

Replies are listed 'Best First'.
Re: DESTROY problem
by JavaFan (Canon) on Dec 31, 2010 at 10:54 UTC
    Yeah, that's why I write that as:
    sub DESTROY { my $self = shift; ... do class specific stuff ... $self->SUPER::DESTROY if $self->can("SUPER::DESTROY"); }
    But that doesn't help if you're doing MI. This does:
    sub DESTROY { my $self = shift; .... foreach my $class (@ISA) { my $destroy = "${class}::DESTROY"; $self->$destroy if $self->can($destroy); } }
    That will do the right thing, regardless how many (even 0) classes you're inheriting.

      I am curious about something I noticed in your solution. How come you can save a method name as a string and use the variable containing the string to call the method, like this:

      use strict; use warnings; use 5.010; {package Cow; sub speak { say "Mooo."; } } my $meth_name = "speak"; Cow->$meth_name; --output:-- Mooo.

      but you can't use the string literal itself to call the method?

      Cow->"speak"; --output:-- String found where operator expected at C:\Users\Me\Documents\perl2.pl + line 13, near "->"speak"" (Missing operator before "speak"?) syntax error at C:\Users\Me\Documents\perl2.pl line 13, near "->"speak +"" Execution of C:\Users\Me\Documents\perl2.pl aborted due to compilation + errors.

        Here's something to ponder. Can you use a quoted string as a subroutine name? If you don't know, figure that out and then you will have your answer.

Re: DESTROY problem
by Corion (Patriarch) on Dec 31, 2010 at 10:49 UTC

    I think the "no error if not found" part only applies to when Perl does it, not when you do it.

    You could work around this by using eval:

    sub DESTROY { ... cleanup ... eval { $self->SUPER::DESTROY(); }; };

    Personally, I simply look at whether the class I'm inheriting from implements a DESTROY method, and if so, I invoke that, and if not, I invoke none. In recent times, I've found inheritance to be too much of a bother anyway and mostly use aggregation and delegation to construct more complex objects.

      I changed my post to simplify it, and the resulting code suggests that what Intermediate Perl says about DESTROY is dead wrong.
Re: DESTROY problem
by chrestomanci (Priest) on Dec 31, 2010 at 16:51 UTC

    I don't know anything about how DESTROY works, but I think your code above may be suffering from a name-space collision with the B module that provides access to the Perl Compiler Backend

      My understanding is that a module must be explicitly (Update: although perhaps indirectly) loaded via use, require or do for any name collision to occur.

      The example code of the OP seems to have been changed in some way to make it 'clearer', and the exact invocation of the program is not given, but no such loading of the B module seems to have been done, so no name collision could happen.

        See this
        $ perl -le"print for grep /^\w+::/, keys %:: version:: Tie:: utf8:: re:: CORE:: DynaLoader:: mro:: Win32CORE:: attributes:: Regexp:: UNIVERSAL:: main:: Win32:: PerlIO:: IO:: Internals:: DB::
        Its not inconceivable that B could be loaded without an explicit use B;
Re: DESTROY problem
by ambrus (Abbot) on Jan 01, 2011 at 20:45 UTC

    Did anyone mention yet that you could call $self->NEXT::DESTROY instead of $self->SUPER::DESTROY? That quietly does nothing if there's no DESTROY method in the superclass.