ЭЛЕКТРОННАЯ БИБЛИОТЕКА КОАПП |
Сборники Художественной, Технической, Справочной, Английской, Нормативной, Исторической, и др. литературы. |
5.3 ObjectsFirst of all, you need to understand packages and modules as previously described in this chapter. You also need to know what references and referenced thingies are in Perl; see Chapter 4, References and Nested Data Structures, for that. It's also helpful to understand a little about object-oriented programming (OOP), so in the next section we'll give you a little course on OOL (object-oriented lingo). 5.3.1 Brief Refresher on Object-Oriented ProgrammingAn object is a data structure with a collection of behaviors. We generally speak of behaviors as being performed by the object directly, sometimes to the point of anthropomorphizing the object. For example, we might say that a rectangle "knows" how to display itself on the screen, or "knows" how to compute its own area. An object gets its behaviors by being an instance of a class. The class defines methods that apply to all objects belonging to that class, called instance methods. The class will also likely include instance-independent methods, called class methods.[9] Some class methods create new objects of the classes, and are called constructor methods (such as "create a new rectangle with width 10 and height 5"). Other class methods might perform operations on many objects collectively ("display all rectangles"), or provide other necessary operations ("read a rectangle from this file").
A class may be defined so as to inherit both class and instance methods from parent classes, also known as base classes. This allows a new class to be created that is similar to an existing class, but with added behaviors. Any method invocation that is not found in a particular class will be searched for in the parent classes automatically. For example, a rectangle class might inherit some common behaviors from a generic polygon class. While you might know the particular implementation of an object, generally you should treat the object as a black box. All access to the object should be obtained through the published interface via the provided methods. This allows the implementation to be revised, as long as the interface remains frozen (or at least, upward compatible). By published interface, we mean the written documentation describing how to use a particular class. (Perl does not have an explicit interface facility apart from this. You are expected to exercise common sense and common decency.) Objects of different classes may be held in the same variable at different
times. When a method is invoked on the contents of the variable, the
proper method for the object's class gets selected automatically. If, for
example, the Admittedly, there's a lot more to objects than this, and a lot of ways to find out more. But that's not our purpose here. So, on we go. 5.3.2 Perl's ObjectsHere are three simple definitions that you may find reassuring:
We'll cover these points in more depth now. 5.3.3 An Object Is Simply a Referenced ThingyPerl doesn't provide any special syntax for constructors. A constructor is merely a subroutine that returns a reference to a thingy that it has blessed into a class, generally the class in which the subroutine is defined. The constructor does this using the built-in bless function, which marks a thingy as belonging to a particular class. It takes either one or two arguments: the first argument is a regular hard reference to any kind of thingy, and the second argument (if present) is the package that will own the thingy. If no second argument is supplied, the current package is assumed. Here is a typical constructor: package Critter; sub new { return bless {}; } The sub new { my $obref = {}; # ref to empty hash bless $obref; # make it an object in this class return $obref; # return it } Once a reference has been blessed into a class, you can invoke the class's instance methods upon it. For example: $circle->draw(); We'll discuss method invocation in more detail below. Sometimes constructors call other methods in the class as part of the
construction. Here we'll call an sub new { my $self = {} bless $self; $self->_initialize(); return $self; } If you want your constructor method to be (usefully) inheritable, then
you must use the two-argument form of bless. That's because, in
Perl, methods execute in the context of the original base class rather
than in the context of the derived class. For example, suppose you have
a Polygon class that had a sub new { my $class = shift; my $self = {}; bless $self, $class; # bless $self into the designated class $self->_initialize(); # in case there's more work to do return $self; } Within the class package, methods will typically deal with the reference as an ordinary (unblessed) reference to a thingy. Outside the class package, the reference should generally be treated as an opaque value that may only be accessed through the class's methods. (Mutually consenting classes may of course do whatever they like with each other, but even that doesn't necessarily make it right.) A constructor may re-bless a referenced object currently belonging to another class, but then the new class is responsible for all cleanup later. The previous blessing is forgotten, as an object may only belong to one class at a time. (Although of course it's free to inherit methods from many classes.) A clarification: Perl objects are blessed. References are not. Thingies know which package they belong to. References do not. The bless operator simply uses the reference in order to find the thingy. Consider the following example: $a = {}; # generate reference to hash $b = $a; # reference assignment (shallow) bless $b, Mountain; bless $a, Fourteener; print "\$b is a ", ref($b), "\n"; This reports 5.3.4 A Class Is Simply a PackagePerl doesn't provide any special syntax for class definitions. You just use a package as a class by putting method definitions into the class. Within each package a special array called
If a missing method is found in one of the base classes, Perl internally
caches that location in the current class for efficiency, so the next time
it has to find the method, it doesn't have to look so far. Changing
If a method isn't found but an If neither a method nor an If that method still doesn't work, Perl finally gives up and complains by raising an exception. Perl classes do only method inheritance. Data inheritance is left
up to the class itself. By and large, this is not a problem in Perl,
because most classes model the attributes of their object using
an anonymous hash. All the object's data fields (termed "instance
variables" in some languages) are contained within this anonymous hash instead of
being part of the language itself. This hash serves as its own little
namespace to be carved up by the various classes that might want to do
something with the object. For example, if you want an object called
5.3.5 A Method Is Simply a SubroutinePerl doesn't provide any special syntax for method definition. (It does provide a little syntax for method invocation, though. More on that later.) A method expects its first argument to indicate the object or package it is being invoked on. 5.3.5.1 Class methodsA class method expects a class (package) name as its first argument. (The class name isn't blessed; it's just a string.) These methods provide functionality for the class as a whole, not for any individual object instance belonging to the class. Constructors are typically written as class methods. Many class methods simply ignore their first argument, since they already know what package they're in, and don't care what package they were invoked via. (These aren't necessarily the same, since class methods follow the inheritance tree just like ordinary instance methods.) Another typical use for class methods might be to look up an object by some nickname in a global registry: sub find { my ($class, $nickname) = @_; return $objtable{$nickname}; } 5.3.5.2 Instance methodsAn instance method expects an object reference[11]
as its first argument.
Typically it shifts the first argument into a private variable (often
called
sub display { my $self = shift; my @keys; if (@_ == 0) { # no further arguments @keys = sort keys(%$self); } else { @keys = @_; # use the ones given } foreach $key (@keys) { print "\t$key => $self->{$key}\n"; } } Despite being counterintuitive to object-oriented novices, it's a good idea not to check the type of object that caused the instance method to be invoked. If you do, it can get in the way of inheritance. 5.3.5.3 Dual-nature methodsBecause there is no language-defined distinction between definitions of class methods and instance methods (nor arbitrary functions, for that matter), you could actually have the same method work for both purposes. It just has to check whether it was passed a reference or not. Suppose you want a constructor that can figure out its class from either a classname or an existing object. Here's an example of the two uses of such a method: $ob1 = StarKnight->new(); $luke = $ob1->new(); Here's how such a method might be defined. We use the ref function
to find out the type of the object the method was called on so our new
object can be blessed into that class. If ref returns false, then
our package StarKnight; sub new { my $self = shift; my $type = ref($self) || $self; return bless {}, $type; } 5.3.6 Method InvocationPerl supports two different syntactic forms for explicitly invoking class or instance methods.[12] Unlike normal function calls, method calls always receive, as their first parameter, the appropriate class name or object reference upon which they were invoked.
The first syntax form looks like this:
Since this is similar to using the filehandle specification with
print or printf,
and also similar to English sentences like "Give
the dog the bone," we'll call it the indirect object form. To look
up an object with the class method $fred = find Critter "Fred"; display $fred 'Height', 'Weight'; The indirect object form allows a display { find Critter "Fred" } 'Height', 'Weight'; The second syntax form looks like this:
This second syntax employs the $fred = Critter->find("Fred"); $fred->display('Height', 'Weight'); Or, you can put the above in only one statement, like this: Critter->find("Fred")->display('Height', 'Weight'); There are times when one syntax is more readable, and times when the other syntax is more readable. The indirect object syntax is less cluttered, but it has the same ambiguity as ordinary list operators. If there is an open parenthesis following the class or object, then the matching close parenthesis terminates the list of arguments. Thus, the parentheses of new Critter ('Barney', 1.5, 70); are assumed to surround all the arguments of the method call, regardless of what comes afterward. Therefore, saying new Critter ('Bam' x 2), 1.4, 45; would be equivalent to Critter->new('Bam' x 2), 1.4, 45; which is unlikely to do what you want since the There may be occasions when you need to specify which class's method to use. In that case, you could call your method as an ordinary subroutine call, being sure to pass the requisite first argument explicitly: $fred = MyCritter::find("Critter", "Fred"); MyCritter::display($fred, 'Height', 'Weight'); However, this does not do any inheritance. If you merely want to specify that Perl should start looking for a method in a particular package, use an ordinary method call, but qualify the method name with the package like this: $fred = Critter->MyCritter::find("Fred"); $fred->MyCritter::display('Height', 'Weight'); If you're trying to control where the method search begins and you're
executing in the class package itself, then you may use the $self->SUPER::display('Height', 'Weight'); The Sometimes you want to call a method when you don't know the method name ahead of time. You can use the arrow form, replacing the method name with a simple scalar variable (not an expression or indexed aggregate) containing the method name: $method = $fast ? "findfirst" : "findbest"; $fred->$method(@args); We mentioned that the object-oriented notation is less syntactically
ambiguous than the indirect object notation, even though the latter
is less cluttered. Here's why:
An indirect object is limited to a name, a scalar variable, or a
A: method $obref->{fieldname} B: (method $obref)->{fieldname} C: $obref->{fieldname}->method() D: method {$obref->{fieldname}} In A and B, the method applies to 5.3.7 DestructorsWhen the last reference to an object goes away, the object is
automatically destroyed. (This may even be after you exit, if you've
stored references in global variables.) If you want to capture control
just before the object is freed, you may define a Perl does not do nested destruction for you. If your constructor
re-blessed a reference from one of your base classes, your 5.3.8 Method AutoloadingAfter Perl has vainly looked through an object's class package and the packages of
its base classes to find a method, it also checks for an use Person; $him = new Person; $him->name("Jason"); $him->age(23); $him->peers( ["Norbert", "Rhys", "Phineas"] ); printf "%s is %d years old.\n", $him->name, $him->age; print "His peers are: ", join(", ", @{$him->peers}), ".\n"; The Person class implements a data structure containing three fields:
package Person; use Carp; # see Carp.pm in Chapter 7 my %fields = ( name => undef, age => undef, peers => undef, ); sub new { my $that = shift; my $class = ref($that) || $that; my $self = { %fields, }; bless $self, $class; return $self; } sub AUTOLOAD { my $self = shift; my $type = ref($self) || croak "$self is not an object"; my $name = $AUTOLOAD; $name =~ s/.*://; # strip fully-qualified portion unless (exists $self->{$name} ) { croak "Can't access `$name' field in object of class $type"; } if (@_) { return $self->{$name} = shift; } else { return $self->{$name}; } } As you see, there isn't really a method named
5.3.9 A Note on Garbage CollectionHigh-level languages typically allow the programmers to dispense with worrying about deallocating memory when they're done using it. This automatic reclamation process is known as garbage collection. For most purposes, Perl uses a fast and simple, reference-based garbage collection system. One serious concern is that unreachable memory with a non-zero reference count will normally not get freed. Therefore, saying this is a bad idea: { # make $a and $b point to each other my($a, $b); $a = \$b; $b = \$a; } or more simply: { # make $a point to itself my $a; $a = \$a; } When a block is exited, its my variables are normally freed up. But their internal reference counts can never go to zero, because the variables point at each other or themselves. This is circular reference. No one outside the block can reach them, which makes them useless. But even though they should go away, they can't. When building recursive data structures, you'll have to break the self-reference yourself explicitly if you don't care to cause a memory leak. For example, here's a self-referential node such as one might use in a sophisticated tree structure: sub new_node { my $self = shift; my $class = ref($self) || $self; my $node = {}; $node->{LEFT} = $node->{RIGHT} = $node; $node->{DATA} = [ @_ ]; return bless $node, $class; } If you create nodes like this, they (currently)[15] won't ever go away unless you break the circular references yourself.
Well, almost never. When an interpreter thread finally shuts down (usually when your program exits), then a complete pass of garbage collection is performed, and everything allocated by that thread gets destroyed. This is essential to support Perl as an embedded or a multithreadable language. When a thread shuts down, all its objects must be properly destructed, and all its memory has to be reclaimed. The following program demonstrates Perl's multi-phased garbage collection: #!/usr/bin/perl package Subtle; sub new { my $test; $test = \$test; # Create a self-reference. warn "CREATING " . \$test; return bless \$test; } sub DESTROY { my $self = shift; warn "DESTROYING $self"; } package main; warn "starting program"; { my $a = Subtle->new; my $b = Subtle->new; $$a = 0; # Break this self-reference, but not the other. warn "leaving block"; } warn "just exited block"; warn "time to die..."; exit; When run as /tmp/try, the following output is produced: starting program at /tmp/try line 18. CREATING SCALAR(0x8e5b8) at /tmp/try line 7. CREATING SCALAR(0x8e57c) at /tmp/try line 7. leaving block at /tmp/try line 23. DESTROYING Subtle=SCALAR(0x8e5b8) at /tmp/try line 13. just exited block at /tmp/try line 26. time to die... at /tmp/try line 27. DESTROYING Subtle=SCALAR(0x8e57c) during global destruction. Notice that "global destruction" in the last line? That's the thread garbage collector reaching the unreachable. Objects are always destructed even when regular references aren't, and in fact
are destructed in a separate pass before ordinary references. This is an
attempt to prevent object destructors from using references that have
themselves been destructed. Plain references are (currently) only garbage
collected if the "destruct level" is greater than 0, which is usually only
true when Perl is invoked as an embedded interpreter. You can test the
higher levels of global destruction in the regular Perl executable by
setting the |