Previous Page
Next Page

15.8. Destructors

Always provide a destructor for every inside-out class.

The many advantages of inside-out classes described earlier come at almost no performance cost. Almost. The one respect in which they are marginally less efficient is their destructor requirements.

Hash-based classes often don't even have a destructor requirement. When the object's reference count decrements to zero, the hash is automatically reclaimed, and any data structures stored inside the hash are likewise cleaned up. This technique works so well that many OO Perl programmers find that they never need to write a DESTROY( ) method; Perl's built-in garbage collection handles everything just fine.

The only time that hash-based classes do need a destructor is when their objects are managing resources that are external to the objects themselves: databases, files, system processes, hardware devices, and so on. Because the resources aren't inside the objects (or inside the program, for that matter), they aren't affected by the object's garbage collection. Their "owner" has ceased to exist, but they remain: still reserved for the use of the program in question, but now completely unbeknownst to it.

So the general rule for Perl classes is: always provide a destructor for any object that manages allocated resources that are not actually located inside the object.

But the whole point of an inside-out object is that its attributes are stored in allocated hashes that are not actually located inside the object. That's precisely how it achieves secure encapsulation: by not sending the attributes out into the client code.

Unfortunately, that means when an inside-out object is eventually garbage-collected, the only storage that is reclaimed is the single blessed scalar implementing the object. The object's attributes are entirely unaffected by the object's deallocation, because the attributes are not inside the object, nor are they referred to by it in any way.

Instead, the attributes are referred to by the various attribute hashes in which they're stored. And because those hashes will continue to exist until the end of the program, the defunct object's orphaned attributes will likewise continue to exist, safely nestled inside their respective hashes, but now untended by any object. In other words, when an inside-out object dies, its associated attribute hashes leak memory.

The solution is simple. Every inside-out class has to provide a destructor that "manually" cleans up the attributes of the object being destructed. Example 15-4 shows the necessary addition to the File::Hierarchy class from Example 15-2.

Example 15-4. An inside-out class with its necessary destructor

package File::Hierarchy;
use Class::Std::Utils;
{
    
# Objects of this class have the following attributes...
my %root_of;
# The root directory of the file hierarchy
my %files_of;
# An array storing an object for each file in the root directory

    # Constructor takes path of file system root directory...
sub new {
# [As in
Example 15-2
]
}

    # Retrieve files from root directory...
sub get_files {
# [As in
Example 15-2
]
}

    # Clean up attributes when object is destroyed...
sub DESTROY { my ($self) = @_; delete $root_of{ident $self}; delete $files_of{ident $self}; return; } }

The obligation to provide a destructor like this in every inside-out class can be mildly irritating, but it is still a very small price to pay for the considerable benefits that the inside-out approach otherwise provides for free. And the irritation can easily be eliminated by using the appropriate class construction tools, as explained under "Automating Class Hierarchies" in Chapter 16.

    Previous Page
    Next Page